文章

将 macOS App 移植到 Apple 芯片

为您的 macOS app 创建可同时在搭载 Apple 芯片和 Intel 芯片的 Mac 电脑上运行的版本。

概览

创建通用二进制文件,并修改代码来处理架构差异,从而将现有的 macOS app 移植到 Apple 芯片。通用二进制文件与普通 app 并无不同,但它的可执行文件中包含您编译的代码的两个版本。一个版本在 Apple 芯片上原生运行,另一个版本则在搭载 Intel 芯片的 Mac 电脑上原生运行。在运行时,系统会自动选取要在当前平台上运行的版本。

插图中显示了一个 app,其主要可执行文件支持 arm64 和 x86_64 架构。

要构建通用二进制文件,您需要 Xcode 12 或更高版本,这类版本将 arm64 添加至 macOS 二进制文件的标准构建架构列表中。当您打开项目并进行干净构建时,如果您的项目使用标准架构,Xcode 会自动创建通用二进制文件。如果您使用自定 makefile 或构建脚本,可以向构建系统添加 arm64 架构。

创建通用二进制文件后,在两种架构中进行测试,并确定您有无必要进行其他更改。macOS 框架可使 app 免受平台间的大多数架构差异的影响,但一些差异仍然需要您更改代码。另外,架构差异也可能会影响 app 性能并需要做进一步更改。

要了解如何构建通用二进制文件,请参阅“构建通用 macOS 二进制文件 (英文)”。

制定移植计划

在移植流程的早期,确定您要用于构建和测试代码的工作流程。Xcode 可在所有 Mac 电脑上运行,因此您可在搭载 Apple 芯片或 Intel 芯片的 Mac 电脑上构建代码,并在那里进行初步测试。但请务必在两种电脑类型上测试、调节和验证代码,以发现特定于某种架构的问题。

插图显示了用于在搭载 Apple 芯片的 Mac 电脑和搭载 Intel 芯片的 Mac 电脑上构建、调试和调节代码的流程。

除了工作流程计划外,也要确定在移植过程中要调查的潜在区域。向 arm64 移植所需的工作量取决于您对硬件相关功能的依赖程度。如果您在很大程度上依赖于 Apple 框架和技术,移植工作量可能很小。如果您专门为 x86_64 架构和硬件功能调节了代码,那么移植到 arm64 可能需要额外的工作。

要开始调查,请记录负责以下事项的所有代码:

  • 与不归您所有的第三方资源库的交互。

  • 与内核或硬件的交互。

  • 依赖于特定 GPU 的行为。

  • 包含汇编的指令。

  • 管理线程或优化 app 的多线程行为。

  • 包含硬件相关的断言或性能优化。

以上列表并非完整的清单,但您的调查可以从这里着手开始。硬件和架构差异可能会给代码在 Apple 芯片运行带来错误或性能问题。尽早识别潜在问题区域可以节省日后的时间。

请务必制定一套完善的测试计划,最好是使用一系列可在构建时运行的自动测试套件。除了测试代码的正确性外,还要收集 app 性能方面的指标。检查 app 的内存使用情况,并且测量它在搭载 Apple 芯片和 Intel 芯片的 Mac 电脑上运行特定任务时分别花费多少时间。利用这些信息来明确有待调查的其他区域。

获取链接资源库的通用版本

如果您的项目依赖于任何第三方资源库,请联系原始供应商,并请他们提供这些资源库的通用版本。在同一进程中运行的所有代码必须支持相同的架构。如果没有链接资源库的通用版本,您就无法生成通用版本的二进制文件。如果有一个或多个资源库不是通用版本,链接器会报告错误。

插图中显示了链接器从 app 的代码和通用资源库创建通用 app。

要了解如何自行创建通用资源库,请参阅“构建通用 macOS 二进制文件 (英文)”。

将插件更新为通用二进制文件

通用插件可在任何 Mac 电脑上原生运行。如果 app 支持插件模型,请为您管理的插件创建通用版本。如果您的公司允许外部开发者贡献插件,请鼓励这些开发者为其插件创建通用版本。

如果 app 将插件直接载入到其进程空间内,则通用插件是不可或缺的。在同一进程中运行的代码必须支持相同的架构。如果 app 试图载入架构不兼容的插件,系统会在载入时报告错误。

插图中显示了为 app 管理插件的 XPC 服务。

在进程外使用 XPC 服务运行的插件可以使用有别于 app 本身的架构来运行。要给开发者时间来更新他们的插件,请提供两个非通用 XPC 服务,分别用来运行 arm64 插件和 x86_64 插件。单个 XPC 服务可以管理原生或转译的插件,但不能同时管理这两者。在创建服务时,为每一服务提供一个唯一套装标识符,以便它们可以同时运行。

有关如何利用 XPC 与进程外插件通信的更多信息,请参阅“XPC (英文)”。

解决架构差异

除了对处理器和图形硬件的大幅更改外,搭载 Apple 芯片和 Intel 芯片的 Mac 电脑之间也存在着细微的架构差异。在移植过程中,请审核您的代码,以确定任何潜在架构问题的修复方案。例如,寻找代码依赖于特定硬件功能或配置的地方。

以下列表明确列出搭载 Apple 芯片和 Intel 芯片的 Mac 电脑之间的几处已知架构差异。如有代码依赖于以下任何一项,请进行相应更新:

  • 虚拟内存页面大小

  • 缓存行大小

  • 可变参数函数

  • 同时为可写和可执行的内存

  • 即时编译器

  • 实时线程

  • 显式线程优先级

  • 硬件相关细节

  • 汇编语言指令或内建原语

  • 矢量单元指令

  • C++ ABI 细节

有关架构差异的更多信息,请参阅“在 macOS 代码中解决架构差异 (英文)”。

更新 GPU 相关代码

Apple 芯片上的 Metal 同时支持搭载 Intel 芯片的 Mac 电脑和 iOS 设备的功能。如果 app 采用了仅可在搭载 Intel 芯片的 Mac 电脑上找到的 Metal 功能,也请考虑在 arm64 代码中采用 iOS 相关的功能。采用这些功能可以为许多 app 带来性能提升。

如果 app 使用 Metal、OpenGL 或 OpenCL,请注意以下差异:

  • Apple 芯片上的 GPU 和 CPU 共享内存。

  • OpenGL 已弃用,但可在 Apple 芯片上使用。

  • OpenCL 已弃用,但在以 GPU 为目标时可在 Apple 芯片上使用。OpenCL CPU 设备不适用于 arm64 app。

有关如何更新图形代码的信息,请参阅“将 Metal 代码移植到 Apple 芯片 (英文)”。

更新驱动程序、系统扩展和内核扩展

将代码移植到 macOS 11 时,请注意与内核交互的代码应满足以下要求:

  • 利用 DriverKit 实施硬件驱动程序。有相关支持可用时,macOS 11 要求您使用 DriverKit 扩展。大多数驱动程序类型现在支持 DriverKit,只有少数仍需要创建内核扩展。

  • 内核扩展必须支持原生架构。内核扩展在内核中运行,而内核则必须始终作为原生进程来运行。无法利用 Rosetta 转译来运行内核扩展。

  • 安装和卸载内核扩展需要重启系统。在安装内核扩展时,系统只有重启之后才会载入您的扩展。

有关内核扩展和驱动程序更改的更多信息,请参阅“实施驱动程序、系统扩展和内核扩展 (英文)”。

从特定的技术迁移

macOS 包含几项目前已弃用或不建议在现行开发时使用的技术。如果您的 app 使用了以下某种技术,请尽快迁移到适当的替代技术:

Apple 芯片仍然支持上述技术,您也可以继续在 macOS 11 中使用它们。但是,这项支持可能会在 macOS 未来某个版本中去除,因此建议您迁移到更新的技术。

调试和测试代码

Apple 芯片支持搭载 Intel 芯片的 Mac 电脑上找到的所有调试和测试工具。Xcode IDE 可用来设置和监控端点,并且监控其他方面的 app 行为。命令行中的 lldb 可用来在 Xcode 界面之外执行类似的任务。

有关如何调试和测试代码的更多信息,请参阅“Xcode”。

调节 App 的性能

Apple 芯片可以运行搭载 Intel 芯片的 Mac 电脑上找到的所有性能工具。可以使用 Instruments 和其他性能工具来收集 app 的各种指标,包括内存使用量、速度和能耗等方面的信息。也可以使用 leaksheaptopfs_usagesc_usagevm_statotoolsamplemalloc_historyvmmap 等命令行工具来识别潜在的性能问题。

由于 arm64x86_64 之间存在架构差异,一种系统上效果良好的技巧可能会在另一种系统上表现不佳。例如:

  • 不要断定独立 GPU 一定性能更强。Apple 处理器中的集成式 GPU 已针对高性能图形任务进行了优化。请参阅“将 Metal 代码移植到 Apple 芯片 (英文)”。

  • 不要断定所有处理器核心均相同。Apple 芯片上的处理器混合使用了性能核心 (P-core) 和效率核心 (E-core),用来执行具有不同性能特点的任务。可以使用服务质量 (QoS) 类来帮助系统将任务调度到正确类型的核心。

在移植过程中,请测量 app 在搭载 Apple 芯片和搭载 Intel 芯片的 Mac 电脑上的性能,并调查任何差异。在一个平台上运行用时较长的任务可能需要更多调节。

有关调节通用二进制文件的具体提示,请参阅“面向 Apple 芯片调节代码的性能 (英文)”。

相关主题

其他移植提示

另请参阅

基础知识