应用生态的支持对于终端消费者而言至关重要,而原生应用的支持能提供更好的性能和更低的功耗。微软为了进一步推动Windows on Arm原生应用的支持,在Windows 11 on Arm引入了新的Arm64EC (“Emulation Compatible”) 应用程序二进制接口支持,从而能够帮助开发人员逐步实现从x64向Arm64架构的应用迁移。
微软的Visual Studio开发工具已经提供了Arm64EC的支持,而广受软件厂商和开发人员喜爱的LLVM开源工具链还没有提供相关的支持。此芯科技正在和高通、Linaro紧密合作一起推动LLVM Clang对Arm64EC的编译支持,虽然没有官方文档的支持,我们已经在本地成功运行使用Clang编译的Arm64EC程序,相关代码也正在合入LLVM上游社区。后续此芯科技将投入资源进一步推动Windows Arm64原生应用的支持,与合作伙伴一起实现生态共赢。
当一个程序以原生Arm64模式运行时,它所调用的所有库都必须拥有同样的原生模式。很多应用程序需要用到一些第三方的动态链接库,如果这个库只有x64的版本,即使开发者可以把程序本体编译成原生Arm64程序,可是由于缺少原生第三方库的支持,整个程序都没办法使用Arm64版本。反之亦然,如果一个对于性能敏感并且广泛应用于很多程序的库获得Arm64原生支持,我们无法通知所有应用这个库的程序也配合进行更新。这样就局限了Arm64原生程序的普及空间。
Arm64EC技术就是用来解决上述问题的。当我们把一个程序编译成Arm64EC的版本,这个程序可以理解为非常接近原生版本的程序,但它还可以直接调用x64的动态链接库,同时如果一个库被编译成Arm64EC的版本,这个库也可以被x64的程序直接调用。
基于Arm64EC,一个完整的应用程序可以被分成多个部分, 并以增量的方式逐步替换成Arm64版本,到最终实现全面替换。另外,我们也可以只替换对性能影响最关键的部分,从而通过最少的投入来获得可以接受的性能提升。
1. 针对Arm64EC编译出的代码是原生代码,但Arm64EC整体程序是跑在x64模拟器上的。模拟器会判断程序是否是x64代码,如果是x64代码模拟器会去模拟执行,如果是Arm64EC代码则经过一些状态保存恢复之后直接跳转过去执行。
2. Arm64EC的实现实际上是把x64的寄存器一一对应到Arm64的寄存器。对应关系大致如下:
由于x64实际的整数通用寄存器只有16个而Arm64的整数寄存器有31个,导致X13,X14,X23,X24,X28这几个寄存器在Arm64EC程序中是不能被使用的。所以Arm64EC和Arm64原生程序相比还是有一些性能差距。
3. 由于Arm64和x64的函数调用约定是不一样的,所以当程序从x64代码去调用Arm64EC代码时,我们需要一个专门的函数去进行调用约定的转换,反之也是需要的。
我们可以通过下面的例子来演示x64应用向Arm64EC迁移的过程。在此过程中的每一步,应用程序都能继续正常运行,而无需一次重新编译所有内容。
(图片来源:Arm64EC for Windows 11 apps on Arm | Microsoft Learn)
1. 从完全模拟的 x64 工作负载开始
2. 将最占用 CPU 的部分重新编译为 Arm64EC
通过将花费最多时间或CPU 密集型的模块从 x64 重新编译到 Arm64EC,生成的工作负载在每一步中以最少的工作量获得最大的改进。
3. 随着时间的推移继续重新编译更多 x64 模块
4. 完成Arm64EC应用迁移的最终结果
上图演示了一个在Windows on Arm机器上完全模拟的 x64应用程序运行的简化示例,该应用程序有不同的模块组成,通过将这些x64模块分阶段编译成Arm64 EC模块,我们看到整个应用程序的性能得到了逐步的提高,最终使得x64的程序完成向Arm64EC程序的转变,在Windows on Arm的机器上获得和原生应用接近的性能。