Swift 进阶 1m
改善 Swift 的内存使用和性能
Improve memory usage and performance with Swift
2025年6月9日
一句话判断
从 700 倍性能差距到丝滑——用 Instruments 定位性能瓶颈,然后用 InlineArray、Span、OutputSpan 把 retain/release 和内存分配统统干掉。
这场 Session 讲了什么
Nate Cook 用一个 QOI 图片解析器做 demo,从一个实际的性能问题出发,手把手带你做五层优化:
- 算法修正:
Data.dropFirst()实际上是 O(n) 复制,换成popFirst()就行。这个低级错误让解析时间从线性变成了二次。 - 消除多余分配:把
flatMap+prefix链式调用重写为预分配Data+ 逐像素写入,从近百万次分配降到个位数。 - 消除 exclusivity 运行时检查:把 class 里的属性挪到 struct 里,
swift_beginAccess检查就消失了。 - InlineArray 替代固定大小 Array:Swift 6.2 新增的
InlineArray用 value generics 把大小编译进类型,元素存在栈上,零引用计数。 - Span 替代 Data/Array:
RawSpan、OutputSpan是 non-escapable 类型,编译器保证生命周期安全,消除了 retain/release 开销。
值得深挖的点
-
InlineArray 是 value generics 的首次实战应用。
InlineArray<64, RGBA>把 64 个元素存在栈上,不需要 heap 分配,不需要 copy-on-write,不需要引用计数。适合固定大小、原地修改的场景。 -
non-escapable 类型改变了性能游戏规则。
Span系列类型用编译期生命周期保证替代了运行时引用计数。用在 tight loop 里效果巨大——demo 里 retain/release 各占 7% 的采样,换完直接归零。 -
OutputSpan 用于渐进式写入。
Data(rawCapacity:)提供一个OutputSpan,你可以往里面 append 数据,不需要手动跟踪 offset,比 unsafe buffer pointer 安全,比Data高效。 -
全新的 Swift Binary Parsing 库。Apple 内部已经在用,基于这些新特性构建,提供安全的二进制格式解析工具。
代码片段
用 InlineArray 替代固定大小的 Array:
// 之前:堆分配 + 引用计数
var pixelCache: [RGBA] = Array(repeating: .zero, count: 64)
// 之后:栈上分配,零引用计数
var pixelCache: InlineArray<64, RGBA> = .init(repeating: .zero)
用 RawSpan 替代 Data 做解析,消除 retain/release:
// 之前:每次调用都有引用计数开销
func readByte(from data: inout Data) -> UInt8 {
let byte = data.first!
data = data.dropFirst() // O(n) 复制!
return byte
}
// 之后:RawSpan 是 non-escapable,零引用计数
func readByte(from span: inout RawSpan) -> UInt8 {
let byte = span.unsafeLoad(as: UInt8.self)
span = span.extracting(1...)
return byte
}
最佳实践
- 用 Instruments 的 Time Profiler 和 Allocations 两个工具配合看。先用 Time Profiler 找热点,再用 Allocations 看分配来源。开启 Invert Call Tree 能快速定位用户代码。
- 对性能关键路径 profile 时,循环跑 50 次以上,给 Instruments 足够的采样数据。
- 固定大小的数组一律考虑 InlineArray,特别是那些只做原地修改不做拷贝的场景。
- 解析类代码优先用 RawSpan/OutputSpan,不要用 Data 做中间层。Data 的 copy-on-write 在 tight loop 里是性能杀手。
- 用
mx.compile(如果是 MLX 场景)或手动合并计算图节点,减少 GPU kernel launch 次数。
还有什么值得关注
- Swift Binary Parsing 库已经开源,支持 ParserSpan、字节序处理、溢出安全计算等,适合做任何二进制格式解析。
UTF8Span是专门做 Unicode 处理的 non-escapable 类型,做文本解析时可以用。- non-escapable 类型目前还在演进中,未来可能有更多标准库类型支持。
- 参考 session 311 了解 Span 在 C/C++ 互操作中的应用。
Swift