Xcode 9.3 新增能力,优化 Swift 编译生成代码的尺寸
Posted SwiftCafe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xcode 9.3 新增能力,优化 Swift 编译生成代码的尺寸相关的知识,希望对你有一定的参考价值。
在 Swift 4.1 的编译器中,提供了一个新的优化选项。可以减少代码生成尺寸。
对生成的代码进行优化,是很多编译器都提供的功能,Swift 之前版本的编译器其实也提供了优化功能,如果你打开 XCode 项目的 Build Settings
, 就可以找到一个叫做 Optimization Level
的选项,默认情况下,XCode 会对 Release 下的项目默认开启编译器优化:
这个选项在实际编译的时候,会给编译器传递一个 -O
参数,它就代表进行性能优化。
新增的编译选项
XCode 9.3,除了上述的性能优化之外,还提供了另外一个新的选项 -Osize
, 对代码尺寸进行优化:
从上图中可以看到 -O
和 -Osize
两个参数是互斥的,只能选一个。 -O
前面我们说过了,是对代码的执行速度进行优化,但执行速度提升了,就会牺牲一部分代码空间。
反之 -Osize
是专门为节省代码空间这个目的而来的优化选项。 那么他们分别能带来多少收益和损失呢? 这点在 swift.org 的官方 Blog 也有介绍:
-Osize
根据项目不同,大致可以优化掉 5% - 30% 的代码空间占用。 相比 -0
来说,会损失大概 5% 的运行时性能。 如果你的项目对运行速度不是特别敏感,并且可以接受轻微的性能损失,那么 -Osize
就值得一用。
Single File 和 Whole Module
除了 -O
和 -Osize
, 还有另外一个概念也值得说一下。 就是 Single File
和 Whole Module
。 在之前的 XCode 版本,这两个选项和 -O
是连在一起设置的,Xcode 9.3 中,将他们分离出来,可以独立设置:
Single File
和 Whole Module
这两个模式分别对应编译器以什么方式处理优化操作。
Single File
是逐个文件进行优化,它的好处是对于增量编译的项目来说,它可以减少编译时间,对没有更改的源文件,不用每次都重新编译。并且可以充分利用多核 CPU,并行优化多个文件,提高编译速度。
但它的缺点就是对于一些需要跨文件的优化操作,它没办法处理。如果某个文件被多次引用,那么对这些引用方文件进行优化的时候,会反复的重新处理这个被引用的文件,如果你项目中类似的交叉引用比较多,就会影响性能。
Whole Module
则是将项目所有的文件看做一个整体,不会产生 Single File
模式对同一个文件反复处理的问题,并且可以进行最大限度的优化,包括跨文件的优化操作。
缺点是,不能充分利用多核处理器的性能,并且对于增量编译,每次也都需要重新编译整个项目。
XCode 的默认设置使用的是 Whole Module
模式。 我特意搜索了一下,在 Stack Overflow 上面找到了一个比较好的总结,也给大家贴出来参考一下:
对于这个选项,我的理解是,如果没有特殊情况,使用默认的 Whole Module
优化即可。 它会牺牲部分编译性能,但的优化结果是最好的。
何为编译优化
关于编译优化,这话话题其实不小。 简单来说,现在我们用高级语言写出的代码,更大程度上是基于我们人的思维逻辑。 然后通过编译器,变成机器的逻辑。比如现在如果大家开发一个项目,更关注业务逻辑的实现,比如点击购买按钮,能不能正常调用下订单的函数。或者当用户完成某个功能,能不能按照预定的要求弹出评价提示等。
我们现在越来越少的会为怎么写一行代码能够减少电量消耗,或者如何提高多核 CPU 的利用率这类的问题花费精力。编译优化就是在一定程度上帮助我们处理这类问题的功能。
用 swift.org 中一篇 Blog 来举个例子:
var x: Int { return 27 }
}
比如上面这个代码,定义了一个属性 x, 它通过一个函数返回一个整数 27。 如果你开启了编译优化,编译器就有可能将这个属性优化为 inline 函数(因为这个函数体相对简单)。所谓 inline 函数,就是在调用它的地方直接把它展开成代码。比如这样:
print(ins.x)
上述代码实际在编译优化后,就成了这样:
print(27)
那么这样替换能带来什么好处呢,因为在程序真正执行的时候,函数调用的开销要比直接执行某段代码大很多,所以将一些比较小的函数直接优化成 inline 的,就肯定会提高程序运行的效率了。
这就是编译优化的一个例子,上面说的 inline 替换是对性能进行优化,可想而知如果你的代码中多次调用了 ins.x
这个属性,那么他们就都会被替换,我们这个例子中这个函数体还比较简单,如果函数体稍微复杂一些,你的代码总量必然会被编译优化增大。 过多的 inline 虽然会对性能提升有帮助,但无疑会增大代码的尺寸。
这也是程序设计中守恒的一个定律,同样的条件下,空间和性能不可能兼得,需要取舍。
相信通过这个解释,大家应该更能理解 -O
和 -Osize
的区别了。 以官方 Blog 的解释,-O
更着重于优化性能,同时会带来代码空间的增大。 -Osize
着重于代码尺寸,比如官方 Blog 上面就有一点明确的说明,-Osize
对于 inline 函数的优化标准就比 -O
谨慎很多。 从这个角度看, inline 优化少了,代码尺寸自然会变小,同样的运行性能就会稍微降低。
当然,上面只是通过 inline 优化给大家举个例子,目的是帮助大家更好的理解编译优化的运作原理。 实际的编译优化操作,要远比我们这里描述的复杂。
关于 -Osize 的更多介绍,大家也可以看一下 swift.org 这篇 Blog:https://swift.org/blog/osize
总结
XCode 9.3 新增的 -Osize
编译选项,给大家提供了一个新的选择。 这篇文章也通过对它的介绍,给大家分享了关于编译优化知识的一些基本概念,也许会帮你在讨论问题的时候多一些谈资。同样,现在其实有不少项目对空间尺寸的优化需求在增多,我想这也是 -Osize
这个新的编译选项提供出来的原因之一吧。 如果你的项目恰好也有这方面的需求,不妨可以试一下它。
最好,劳烦各位几秒钟,做一个小调查,什么时候你看文章最方便,按照这个调整文章推送时间。
以上是关于Xcode 9.3 新增能力,优化 Swift 编译生成代码的尺寸的主要内容,如果未能解决你的问题,请参考以下文章
Xcode 9.3(Swift 4.1)中的 Codable '没有初始化器'
没有这样的模块 'GoogleSignIn' Xcode 9.3 和 Swift 4.1