关于 swift-runtime-performance
swift-runtime-performance 是用于审查 Swift 运行时开销的 Agent 技能,涵盖分配、ARC、派发、复制和特化。它引导开发者依据测量采取针对性改动,并考虑语义与维护代价。
主要功能
- 01分类运行时开销
- 02确认代码是否处于热点路径
- 03依据性能分析与编译器证据
- 04提出针对性的运行时改动
- 05说明实现权衡
适用场景
- 01审查分配或 ARC 开销
- 02调查派发或泛型特化
- 03评估写时复制成本
- 04检查 Swift 不安全边界
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ livsy90/ ios-performance-agent-skills --skill swift-runtime-performance安装后在 Claude Code 中输入/swift-runtime-performance确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/allocation-and-layout.mdarc-and-ownership.mdconcurrency-runtime.mdcow-and-large-values.mddispatch-and-specialization.mdexistentials-generics-opaque-types.mdmodularization-and-linking.mdsil-inspection.mdunsafe-swift.md详细介绍
Swift 运行时性能
用途
使用此技能审查 Swift 代码的运行时性能开销,不要把每个抽象都视为问题。重点关注具体开销,例如内存分配、ARC 流量、派发、特化、复制、优化器可见性和不安全内存边界。
此技能旨在帮助 Agent 区分真正的热路径运行时开销与理论上的微优化。
何时使用此技能
当任务涉及 Swift 运行时行为时,请使用此技能,例如:
- 堆分配、栈与堆存储、对象布局、装箱值或闭包上下文;
- ARC 保留/释放流量、所有权、生命周期、弱引用/无主引用或闭包捕获;
- 热路径中的直接派发、类派发、Objective-C 派发、协议见证派发或动态派发;
any Protocol、some Protocol、泛型、类型擦除、特化或未特化的热代码;- 写时复制集合、大型值、自定义 COW 存储或重复复制;
- 优化后的 SIL 检查、编译器优化、内联、去虚拟化或特化方面的证据;
- 不安全 Swift、指针生命周期、内存绑定、别名、缓冲区变更,或不安全区域周围的安全封装;
- 影响优化器可见性的模块边界、
@inlinable、@usableFromInline、@frozen或公共 API 弹性方面的权衡。
何时不使用此技能
以下情况请勿使用此技能:
- 不涉及运行时性能问题的一般 Swift 语法或 API 用法问题;
- App 启动调查,主要问题是 pre-main、dyld、静态初始化器、SDK 启动、首帧或首次交互;
- SwiftUI 性能问题,主要问题是身份、失效、状态范围、布局、绘制、动画或滚动;
- Swift Concurrency 问题,主要问题是任务生命周期、Actor 隔离、MainActor 响应能力、取消、AsyncSequence 清理、重入或执行器行为;
- 性能分析工作流问题,主要任务是选择工具、解读跟踪记录、设计 signpost、XCTest 度量、MetricKit 或生产环境信号;
- 广泛的架构问题,除非热路径中存在具体的运行时开销。
如果有更具体的技能,请优先转到该技能,仅在处理运行时子问题时使用此技能。
相邻技能的适用范围
应用运行时建议前,请遵循以下界限:
- 如果任务围绕 Actor、任务、MainActor、取消、AsyncSequence、continuation、任务组、执行器行为或异步工作下的响应能力,请使用
swift-concurrency-performance。 - 如果任务围绕冷启动、热启动、pre-main、dyld、框架加载、静态初始化器、AppDelegate、SwiftUI
App、首帧、首次交互或启动指标,请使用ios-launch-performance。 - 如果任务围绕 SwiftUI body 求值、失效、身份、状态所有权、依赖范围、
List、LazyVStack、布局、绘制、动画或视图生命周期工作,请使用swiftui-performance。 - 如果任务围绕选择 Instruments 模板、解读跟踪记录、XCTest 度量、MetricKit、signpost、内存图、卡顿、顿挫、CPU、分配、磁盘 I/O、网络或生产遥测,请使用
ios-performance-profiling。 - 如果相邻领域的任务发现了 Swift 运行时级别的开销,例如分配频繁、ARC 流量、存在类型装箱、协议见证派发、未特化泛型、重复 COW 复制或不安全内存边界,请使用此技能。
核心原则
不要仅根据源代码的表面形式进行优化。
首先确定:
- 代码是否位于热路径;
- 怀疑存在何种运行时开销;
- 能否通过测量、编译器输出或小型基准测试观察到该开销;
- 建议的改动是否保留语义并改善测量所涉及的路径;
- 该改动会带来何种权衡。
只有当运行时优化能降低重要路径上的开销时,它才有价值。
运行时开销分类
在建议改动前,先对疑似问题进行分类。
使用以下类别:
- 分配 — 堆对象、装箱、闭包上下文、存在类型容器、临时对象、中间集合。
- ARC — retain/release 开销、闭包捕获、weak/unowned 访问、桥接对象生命周期、由引用支持的值存储。
- 分派 — 动态分派、见证表分派、Objective-C 分派、闭包调用、未能去虚拟化。
- 存在类型与泛型 —
any、some、类型擦除、泛型特化、未特化的热点路径、装箱。 - 复制 — COW 存储、大型值、重复修改集合、防御性复制、桥接复制。
- 编译器优化 — 内联、特化、去虚拟化、模块可见性、弹性边界、优化后的 SIL 输出。
- 不安全边界 — 指针生命周期、绑定、对齐、别名、修改、逃逸缓冲区、安全封装。
- 模块边界 — 公共 API 可见性、
@inlinable、@usableFromInline、@frozen、ABI 弹性、优化器可见性。
核心工作流程
- 定位用户可见的症状。 确定问题涉及延迟、滚动、重复工作、内存增长、CPU 使用、二进制大小、启动影响,还是理论上的代码审查风险。
- 确认热点路径。 询问代码是否频繁运行、是否处理大量元素、是否阻塞交互、是否在启动期间运行,或是否出现在测量结果中。
- 对疑似开销进行分类。 使用运行时开销分类,而不是笼统地说代码“很慢”。
- 寻找证据。 优先使用 Instruments、Allocations、Time Profiler、优化后的 SIL、基准测试输出、XCTest 性能测试或生产环境信号。
- 区分语义与机制。 不要仅因抽象可能产生开销就移除它。先确认它所服务的设计目的。
- 提出最小且安全的改动。 优先采用局部改动,以减少分配、ARC 开销、分派、复制或未能特化,同时不损害 API 的清晰度。
- 说明权衡。 提及可读性、API 灵活性、可测试性、二进制大小、ABI 稳定性、构建时间或维护成本。
- 验证结果。 没有前后对比的验证方案,就不要宣称优化成功。
决策规则
如果代码不在热点路径上
避免低层重写。
说明这个顾虑在理论上可能成立,但没有证据时不太可能造成实际影响。只有怀疑该路径会影响用户可见的性能时,才建议进行测量。
如果问题涉及分配
检查分配是否来自:
- 类实例;
- 闭包上下文;
- 装箱变量;
- 存在类型存储;
- 类型擦除包装器;
- 中间数组、字典、集合、字符串或数据缓冲区;
- Swift 与 Objective-C/Foundation 类型之间的桥接。
优先减少重复分配,而不是替换所有引用类型。
如果问题涉及 ARC
建议改动前,先检查所有权和生命周期。
留意循环中的重复 retain/release 开销、闭包捕获的大型所有者、热点路径中不必要的 weak 访问,以及由引用支持的值存储。不要把 weak 和 unowned 当作性能优化手段。它们首先是所有权工具。
如果问题涉及分派
确认动态分派是否有意为之。
对于不面向继承设计的应用级类,优先使用 final。仅当泛型、具体类型或内部实现细节既能保留预期抽象,又对测量到的路径有帮助时,才考虑使用它们。
没有证据时,不要扁平化有用的多态结构。
如果问题涉及 any Protocol
确认是否需要运行时异构性。
any Protocol 并非天然有问题。当必须统一存储或传递不同具体类型的值时,它是合适的。如果热点路径可以保持静态类型,则可考虑使用泛型或不透明类型。
如果问题出在泛型上
检查是否确实发生了特化。
当优化器能看到足够的实现细节来特化热路径时,泛型最能发挥作用。模块边界、公开 API 的稳定性要求、函数过大或类型擦除都可能对此造成限制。
如果问题出在写时复制上
检查变更模式和唯一性边界。
当存储被唯一引用时,反复变更 COW 值的成本可能很低;若反复复制,成本则可能很高。自定义 COW 必须保留值语义,并说明线程安全方面的假设。
如果问题出在 SIL 或编译器输出上
得出性能结论时,应使用优化后的 SIL,而非 Debug SIL。
留意分配指令、retain/release 流量、witness 调度、existential 打开、闭包创建、未能完成的特化,以及未能完成的去虚拟化等证据。应将 SIL 视为证据,而不是过度贴合某个编译器版本来改写源代码的借口。
如果问题出在不安全 Swift 上
不要把不安全代码作为首选方案。
只有当安全 API 无法足够高效地表达该操作、测量结果表明抽象带来的成本确实重要,并且能将不安全代码区域限制在安全封装内时,才使用不安全 API。
如果问题出在模块边界上
将运行时优化与架构和构建时问题分开处理。
只有在 API 承诺可以接受时,才使用 @inlinable、@usableFromInline 和 @frozen。这些属性不是通用的“让它更快”开关。
常见注意事项
struct不保证在栈上分配。class不一定意味着存在性能问题。- 值语义仍可能涉及堆存储和 ARC。
Array、String、Dictionary、Set和Data可能隐藏由引用支持的存储。any Protocol是一种有用的抽象,虽然会带来运行时成本,但并非错误。some Protocol并不能普遍替代any Protocol。- 只有在发生特化时,泛型才能发挥最大作用。
- 对于未设计为支持继承的应用级类,
final是不错的默认选择,但不应将其吹捧为独立的性能修复方案。 @inline(__always)可能增大代码体积,不应作为首选修复方法。@inlinable是对 API 和 ABI 的承诺。weak和unowned是所有权工具,不是性能工具。- Debug 构建中的行为并不能可靠反映优化后的运行时性能。
- 随意使用不安全代码可能会让代码变慢、降低优化效果,甚至导致错误。
- 除非测量结果证明低级代码确有必要,否则不要用它取代易读的架构。
参考资料路由
有选择地阅读参考资料。默认不要加载所有参考文件。
references/allocation-and-layout.md— 当任务涉及栈与堆行为、对象布局、闭包盒、存在类型存储、临时分配,或值类型中的隐藏堆存储时阅读。references/arc-and-ownership.md— 当任务涉及保留/释放开销、闭包捕获、弱/无主引用、对象生命周期、引用循环、COW 所有权或桥接生命周期时阅读。references/dispatch-and-specialization.md— 当任务涉及直接派发、类派发、Objective-C 派发、见证表派发、闭包派发、去虚拟化、内联或泛型特化时阅读。references/existentials-generics-opaque-types.md— 当任务涉及any Protocol、some Protocol、泛型、类型擦除、协议见证表派发、不透明结果类型,或替换存在类型密集的热点路径时阅读。references/cow-and-large-values.md— 当任务涉及写时复制集合、大型结构体、反复修改集合、自定义 COW 存储、唯一性检查或值语义 API 设计时阅读。references/sil-inspection.md— 当仅靠源码层面的推理不够,且任务需要优化后的 SIL 证据来分析分配、ARC、派发、存在类型打开、闭包创建、特化或内联时阅读。references/unsafe-swift.md— 当任务涉及不安全指针、缓冲区访问、内存绑定、对齐、别名、手动生命周期、不安全封装,或用不安全代码替代安全 API 时阅读。references/modularization-and-linking.md— 当任务涉及模块边界上的优化器可见性、公共 API 弹性、@inlinable、@usableFromInline、@frozen、静态与动态库,或模块化带来的运行时权衡时阅读。references/concurrency-runtime.md— 仅当并发相关问题具体涉及 Swift 运行时开销(例如分配、ARC、闭包捕获、SIL 降低或执行器相关开销)时阅读。对于 Actor 设计、MainActor 响应能力、取消、AsyncSequence 清理、延续,或结构化并发工作流,请改用swift-concurrency-performance。
证据与验证
根据疑似开销选择合适的证据:
- 当关注对象频繁创建和销毁、盒、闭包上下文、存在类型存储或临时集合时,使用 Allocations。
- 当关注 CPU 开销、派发开销、复制、桥接或热点函数调用时,使用 Time Profiler。
- 当关注编译器优化、特化、去虚拟化、ARC 插入、存在类型打开、闭包分配或内联时,使用优化后的 SIL。
- 当需要隔离紧密循环、集合操作、派发模式,或泛型与存在类型的差异时,使用小型基准测试。
- 当操作能够确定性地复现时,使用 XCTest 性能测试。
- 当性能声明影响真实用户路径时,使用前后对比追踪。
除非任务专门讨论仅在 Debug 构建中出现的开发性能,否则不要使用 Debug 构建进行验证。
输出要求
审查代码时,请按以下结构回复:
- 总结 — 说明相关问题很可能存在、仅是理论上的,还是无法根据所提供的代码判断。
- 疑似运行时开销 — 将问题归类为分配、ARC、派发、存在类型/泛型、复制、编译器优化、不安全边界或模块边界开销。
- 热路径评估 — 说明这段代码是否可能影响用户可感知的性能。
- 待检查的证据 — 指明可以验证该假设的测量结果、SIL 输出、基准测试或跟踪记录。
- 发现 — 说明具体的源代码模式及其可能产生运行时工作的原因。
- 建议的更改 — 提出最小且安全的更改。仅在有助于说明建议时提供代码。
- 权衡 — 提及可读性、抽象、API 灵活性、二进制大小、ABI 稳定性、构建时间或可维护性。
- 验证 — 说明如何通过更改前后的证据确认结果。
回复风格
精准、克制。
建议使用:
- “这可能会发生分配,因为闭包逃逸了,并捕获了
self。” - “这个存在类型可能没有问题,除非此循环属于热路径,或 Instruments 中出现了分配。”
- “使用优化后的 SIL 检查泛型函数是否跨此模块边界进行了特化。”
- “如果情况集合是封闭的,并且测量表明此路径属于热路径,那么具体枚举可能会有所帮助。”
避免使用:
- “结构体总是更快。”
- “类很慢。”
- “协议不利于性能。”
- “总是用泛型替换
any。” - “用不安全指针来提速。”
- “添加
@inline(__always)。” - “为了性能,把所有内容都标记为
final。”
正向触发示例
遇到以下提示时,应触发此技能:
- “请审查这个 Swift 热路径中的 ARC 和分配开销。”
- “这个信息流存储了
any FeedItem值,这会产生运行时开销吗?” - “这种以协议为主的代码是否应该改用泛型?”
- “这个自定义 COW 类型会不会意外地复制过多内容?”
- “你能检查这份优化后的 SIL,并解释其中的保留和分配吗?”
- “这个跨模块使用的小型泛型函数,使用
@inlinable是否合理?” - “这个大量使用闭包的解析器分配很多,我应该检查什么?”
- “这种不安全缓冲区优化是否合理?”
负向触发示例
遇到以下提示时,不应触发此技能:
- “我的 SwiftUI 列表在行更新时会卡顿。”
- “应用在冷启动后,显示首屏的时间太长。”
- “我应该如何在这个任务组中处理取消?”
- “我应该使用哪个 Instruments 模板来诊断卡顿?”
- “如何创建 Swift 协议?”
- “如何让这个 SwiftUI 界面更好看?”
- “我是否应该将此 SDK 初始化移出 AppDelegate?”
将这些任务交给更具体的技能处理,除非用户明确询问 Swift 运行时级别的开销。