Swift 4.1 中的代码大小优化模式

在 Swift 4.1 中,编译器现在支持一种新的优化模式,该模式启用专用优化以减小代码大小。

Swift 编译器带有强大的优化功能。当使用 -O 编译时,编译器会尝试转换代码,使其以最高性能执行。但是,运行时性能的这种改进有时会以代码大小增加为代价。使用新的 -Osize 优化模式,用户可以选择编译以获得最小的代码大小,而不是最大速度。

要在命令行上启用大小优化模式,请使用 -Osize 而不是 -O。在 Xcode 9.3 中,有一个新的 Swift 编译器代码生成构建设置

Xcode optimization mode settings

此外,编译模式(单文件或整个模块)现在可以独立于优化模式进行选择

Xcode compilation mode settings

-Osize 模式在整个模块以及单文件编译中都有效,而整个模块模式可提供最佳优化结果。

我们已经看到,对于某些项目,使用 -Osize 可以将代码大小减少 5% 甚至 30%。

但是性能如何呢?这完全取决于项目。对于大多数应用程序来说,使用 -Osize 造成的性能损失可以忽略不计,即低于 5%。但对于性能敏感的代码,-O 可能仍然是更好的选择。

对代码优化的影响

让我们深入了解编译器在使用 -Osize 时有何不同之处。使用 -Osize,编译器会像使用 -O 一样优化代码。但与 -O 相比,编译器会尽量避免代码重复。例如,当内联函数时,编译器会使用较低的大小限制来决定是否应该内联函数。

完全禁用函数内联将是一个坏主意,因为内联小函数通常可以减小代码大小。例如,考虑简单的 getter 函数,例如

struct X {
    var x: Int { return 27 }
}

调用此 getter 的调用开销将远高于内联函数。这是一个极端的例子,但事实证明,在一定大小范围内,内联仍然值得,同时还能减小代码大小。此外,函数内联可以触发其他优化,进而可以减小代码大小。例如,在下面的代码片段中,通过内联 getter a.x,我们知道 a.x 的计算结果为 27,因此整个 if 分支可以被优化掉

func foo(a: X) {
    if a.x != 27 {
        // Can be optimized away if the getter of a.x is inlined
    }
}

除了内联之外,编译器还使用 -Osize 执行其他特定于代码大小的优化。例如,用于处理泛型类型或 Objective-C 桥接的一些代码模式被提取到辅助函数中,并且不会内联生成。

结论

新的 -Osize 优化模式是为那些对性能不是超级敏感的程序减小代码大小的好方法。

我们鼓励您尝试 -Osize 并向我们提供反馈。请在论坛中使用 osize 标签分享您的经验。