x86 程式在 Arm 上要付多少代價?Chips and Cheese 實測 Windows Prism 轉譯:效能掉四到五成

發布日期:2026年10月

Windows on Arm 在 PC 市場是不是能站穩,第一件事就是一大堆只有 x86-64 版本的舊軟體能不能「照常跑」。微軟在 Windows 11 24H2 推出的新一代二進位轉譯器 Prism,就是負責把 x86-64 指令即時翻成 aarch64 指令的那層轉譯器。硬體分析網站 Chips and Cheese 近日以 Geekbench 7 的原生 aarch64 版與 x86-64 版對照,實測這層轉譯到底要付出多少代價——答案是 單執行緒分數掉了四到五成,比一般人直覺想像的還要多。


實測結果:效能損失 41.7%~50.2%

測試平台包含 Asus Zenbook A16 上的 Snapdragon X2 Elite Extreme(X2E-96-100),以及 Azure 上的 Arm 伺服器執行個體。Geekbench 7 單執行緒分數如下:

CPU 核心原生 aarch64x86-64 經 Prism 轉譯損失
Snapdragon X2 Elite Extreme P-Core3390192343.27%
Snapdragon X2 Elite Extreme E-Core167797741.74%
Azure Cobalt 100(Neoverse N2)139472747.85%
Ampere Altra(Neoverse N1)87443550.23%

四到五成的損失,等於倒退好幾個世代的 CPU 效能。不過換個角度看,Qualcomm 的高效能核心底子夠厚,足以吸收這個代價:P-Core 在轉譯後拿到 1923 分,仍比 Neoverse N2 的原生分數高出約四成;E-Core 轉譯後也還贏過 Neoverse N1 原生執行。

損失幅度也因工作負載而異。以 P-Core 為例,受分支預測與記憶體延遲限制、本身 IPC 就低的 Navigation 只掉約 14%;但大量使用 AVX 向量指令的 Video Player 掉約 56%,Photo Library 掉約 59%,Photo Editor 更掉了約 64%。

為什麼掉這麼多:指令數大約翻倍

作者以效能計數器量測發現,除了 PDF Viewer 之外,x86-64 程式經轉譯後執行的 aarch64 指令數大致是原本 x86-64 指令數的兩倍。原因包括:

  • 定址模式:x86-64 可在一條指令內完成「基底+索引×比例+位移」的複雜定址,aarch64 常需要額外指令計算位址。
  • 載入與運算合一:x86-64 允許一條指令同時從記憶體載入並做運算,aarch64 必須拆成 load 與運算兩條,可能還要多用一個暫存器。
  • 旗標(flags):許多 x86-64 指令都會設定旗標,aarch64 不一定有對應版本。Prism 傾向忠實地把旗標算出來,而不是花運算成本去分析後面到底有沒有人用。
  • 暫存器對應:Prism 試圖讓 x86 的 xmm 暫存器固定對應到同編號的 NEON 暫存器,因此會多出搬移資料的指令。

寬核心可以靠閒置的執行寬度吃下部分多出來的指令,Qualcomm 核心在轉譯時的 IPC 確實有上升,但升幅遠追不上指令數的膨脹,這就是損失居高不下的主因。

向量化程式最痛:AVX 256-bit 只能拆成兩份 NEON 128-bit

Video Player 最熱的迴圈原本是 17 條 x86-64 指令,用 AVX FMA3 走訪陣列做乘加運算;經 Prism 轉譯後,在 Neoverse N1 與 Snapdragon X2 Elite 上都變成 69 條。原因是 AVX 的 256-bit 運算必須拆成兩條 128-bit 的 NEON 指令來做。例如一條 vbroadcastss 被翻成三條:載入數值、廣播到 NEON 暫存器各 lane、再複製一份到代表 ymm 高 128 位元的另一個 NEON 暫存器。

那 Snapdragon X2 Elite 支援的 SVE 能不能幫上忙?作者認為效果有限:Qualcomm 核心的 SVE 實作向量長度同樣是 128-bit,而且 x86-64 的固定寬度向量指令本來就不適合映射到 SVE 的可變向量長度模型。換句話說,問題不在 NEON 和 SVE 誰比較新,而在 256-bit 的 AVX 遇上 128-bit 的硬體,加上固定長度與可變長度語意對不上。從轉譯出的程式碼來看,Prism 目前產生的仍是 NEON 指令。

作者也點出 Prism 不夠聰明的地方:迴圈中八個 FMA 運算都把代表 ymm7 高半部的 NEON 暫存器「溢出(spill)」到堆疊再讀回來,但這些值在下一輪迴圈前根本沒被修改過,等於白白讓快取頻寬需求加倍。人工翻譯可以省掉這些溢出,也可以省掉硬體預取器本來就能處理的軟體預取指令。不過 Prism 也有做對的地方:八個 FMA 中有六個的位址計算,它識別出 rax + r9*4 已經算過,只用一條減法就解決。

什麼是「dead code」?

文中提到的 dead code,指的是被產生、也實際被執行,但結果從頭到尾沒有被用到的指令。以 Prism 來說,典型例子就是為了忠實模擬 x86 而計算的旗標(例如替右移指令算出 carry flag,後面卻沒有任何指令讀它),以及前面提到不必要的暫存器溢出與讀回。

這類「不在關鍵路徑上的多餘指令」,高效能亂序核心通常可以用閒置的執行寬度平行消化掉。作者指出 Prism 產生的 dead code 並不算多;真正難處理的是為了模擬 x86 定址、或為了維持暫存器對應而多出來的搬移指令,因為它們會拉長關鍵相依鏈,再大的亂序引擎也無能為力。

亂序引擎越大,越能吸收轉譯負擔

Qualcomm 的 E-Core 與 Neoverse N1 同樣是 4-wide、執行資源相近,但 Qualcomm 的設計可以同時保有超過兩倍數量的在途指令(in-flight instructions)。轉譯多出的指令會增加亂序資源的壓力,較大的亂序引擎比較能吸收短暫的延遲,因此 E-Core 的表現把 N1 遠遠甩在後面。作者的結論是:比起核心寬度,亂序引擎大小等其他架構特性更重要。

不過也不是越寬就越好。Qualcomm 的 P-Core 在 Video Player 有明顯的 IPC 提升,但在 Text Processing 與 HDR 只有小幅增加,代表這些工作負載很快就撞到核心寬度以外的瓶頸。

Neoverse N2:L2 一旦 miss,就要面對約 100 cycle 的 L3 延遲

Neoverse N2 原生執行時就沒有任何一項測試的平均 IPC 超過 3,轉譯後 IPC 也幾乎沒有提升。從 Azure 上能取得的 STALL_FRONTEND/STALL_BACKEND 計數器來看,N2 不論前端或後端損失的週期都比 Qualcomm E-Core 多。

原因之一在快取階層:N2 每核心有 1 MB 的 L2,一旦在 L2 miss,就得到 L3 抓資料,延遲約 100 個 cycle;相較之下,Qualcomm 給 E-Core 叢集配了 12 MB、延遲只有 21 cycle 的共享 L2,核心私有快取也更大。Neoverse N1 的情況更糟,L2 miss 延遲超過 100 cycle,加上亂序引擎較小,後端停頓更嚴重。值得注意的是,這部分是架構本身的弱點,轉譯並沒有讓它明顯惡化——但它讓核心本來就沒有多餘的餘裕去吸收轉譯帶來的額外指令。

記憶體順序:用 load-acquire 換取 x86 語意

x86 的記憶體模型(TSO)比 aarch64 嚴格,Prism 的做法是把一般的載入指令翻成 load-acquire,而不是 aarch64 的一般 LDR,以確保順序正確。這也是 Prism 對不同 CPU 產生不同程式碼的例子之一:Snapdragon X2 Elite 支援 FEAT_LRCPC2,可以使用帶立即值位移的 ldapurb,取代 Neoverse N1 上只能用的 ldaprb,省下額外的位址計算指令。微軟官方文件也提到,Prism 的部分效能功能需要只有 Snapdragon X 系列才具備的硬體功能。

轉譯快取:Windows 的 XtaCache 與 Apple 的 Rosetta 2

Prism 會把轉譯後的程式碼存到 C:\Windows\XtaCache,下次啟動同一個執行檔時就不必重新轉譯;這同時讓研究者能直接翻出轉譯結果來分析(雖然微軟沒有公開 .jc 快取格式,是第三方自行解出來的)。

Apple 的 Rosetta 2 走的是類似路線:第一次執行 x86-64 程式時由 oahd 服務做預先轉譯(AOT),結果快取在 /var/db/oah/,受系統完整性保護(SIP),使用者無法直接修改;遇到執行時才產生的程式碼(例如 JIT),則改用即時轉譯。兩者最大的差異在硬體:Apple Silicon 內建可切換的 TSO 記憶體順序模式,Rosetta 2 不必像 Prism 那樣把載入指令改成 load-acquire 來保證順序。(注:Chips and Cheese 原文並未測試 Apple 平台,本段為補充說明。)

對使用者的意義:日常無感,單核重度負載有感

四到五成的損失聽起來嚇人,但以 Snapdragon X2 Elite 這類 9-wide、5 GHz 的高效能核心來說,轉譯後的效能仍高於不少原生執行的 Arm 伺服器核心。作者實際使用 Zenbook A16 的感受是「出乎意料地正常」,例外是遊戲這類極端情境。

換句話說,大部分辦公、瀏覽等日常應用,在目前的高效能 Arm CPU 上透過轉譯執行應該都沒有壓力;但像遊戲這種仰賴單核高效能、又常大量使用 AVX 向量指令的情境,四到五成(向量密集的工作甚至超過五成)的損失就非常可觀。長期解法還是兩條路並進:一是更多應用程式推出原生 aarch64 版本,二是 CPU 本身「沒有明顯短板」——大指令快取(E-Core 128 KB、P-Core 192 KB)、充足的亂序資源,讓轉譯的額外負擔有地方被吸收。


原文精華整理

  • 測試方法:Geekbench 7 原生 aarch64 版 vs. x86-64 版經 Windows 11 Prism 轉譯,每項工作負載跑 200 次;平台為 Snapdragon X2 Elite Extreme X2E-96-100(Asus Zenbook A16)、Azure Cobalt 100(Neoverse N2)、Ampere Altra(Neoverse N1)。
  • 分數損失:P-Core 43.27%、E-Core 41.74%、N2 47.85%、N1 50.23%;Qualcomm 核心損失略小。
  • 指令數:轉譯後 aarch64 指令數約為 x86-64 的兩倍(PDF Viewer 例外);不同 CPU 上的指令數相近。
  • 工作負載差異:低 IPC 的 Navigation 損失最小(P-Core 約 14%),AVX 密集的 Video Player、Photo Editor、Photo Library 損失最大(約 56%~64%)。
  • IPC:Qualcomm 核心靠閒置寬度提升部分 IPC,但追不上指令數膨脹;N2 任何測試平均 IPC 都不到 3,轉譯後也幾乎沒提升。
  • 亂序引擎:Qualcomm E-Core 與 N1 同為 4-wide,但在途指令容量超過兩倍,更能吸收轉譯負擔。
  • 快取:Qualcomm E-Core 叢集 12 MB L2、21 cycle;N2 的 1 MB L2 miss 後面對約 100 cycle 的 L3 延遲;N1 L2 miss 延遲超過 100 cycle。
  • 向量轉譯:AVX 256-bit 被拆成兩組 NEON 128-bit;17 條指令的迴圈變成 69 條;Qualcomm 的 SVE 為 128-bit,且固定寬度的 x86 向量不適合映射到 SVE 可變長度。
  • Prism 的弱點:近乎逐指令的線性翻譯,保留旗標計算、不必要的暫存器溢出、為維持暫存器對應而多出的搬移,部分會拉長關鍵相依鏈。
  • 記憶體順序:一般載入翻成 load-acquire;X2 Elite 支援 FEAT_LRCPC2,可用 ldapurb 省下位址計算。
  • 轉譯快取:存於 C:\Windows\XtaCache(.jc 格式,微軟未公開文件)。
  • 結論:最終解法是「沒有弱點的高效能架構」;X2 Elite 的 9-wide 5 GHz P-Core、大指令快取與充足亂序資源有助於抵銷轉譯負擔,日常使用感受正常,遊戲等情境例外。

原文連結:Chester Lam,〈On Binary Translation and its Consequences〉,Chips and Cheese,2026年9月10日。

補充參考:Microsoft Learn:How emulation works on Arm;Google Cloud:Rosetta 2 Artifacts;The Eclectic Light Company:Explainer: Rosetta 2。