Windows 11のARM版は、Prismエミュレーション層によってx86/x64アプリを変換・実行できます。多くの場合エミュレーションを意識せずに使えるほど完成度が高いのですが、命令セットの選択によっては予想外の性能低下が起きることが明らかになりました。

RemObjectsのコンパイラ開発チームが実施したベンチマークによると、AVX2でコンパイルしたコードをWindows ARMで実行すると、SSE2-4.x版と比べて約3分の2の速度しか出ないという結果が出ています。この検証記事はHacker Newsで101ポイントを獲得し、開発者コミュニティで議論を呼びました。

ベンチマークの設計と測定方法

テストではLLVMベースのコンパイラを使い、21種類の数学演算(sin、pow、exp、floorなど)を64ビット倍精度浮動小数点で実行しました。入力はランダム初期化された配列で、各演算を1,000万回ループさせています。

測定環境はIntel Tiger Lake i7(2021年、x64ネイティブ)とApple M2上のParallels(ARM、Windows 11 Prism経由)です。異なるハードウェアで直接比較はできないため、各環境でSSE2-4.x(x64 v2)の結果を1.0に正規化し、AVX2+FMA(x64 v3)の相対性能を比較する手法が採用されました。

結果:AVX2はネイティブで2.7倍、エミュレーションで0.67倍

SSE2-4.xを基準とした場合、ネイティブIntelでのAVX2は2.7倍の性能向上を示しました。しかし同じAVX2コードをARM上でエミュレーション実行すると、SSE2-4.xの約3分の2(0.67倍)にとどまる結果となっています。

つまり、AVX2対応でビルドしたアプリをWindows ARMで動かすと、古い命令セットでビルドしたものより遅くなってしまうわけです。

なぜAVX2エミュレーションは遅いのか

主な要因として、ARMのNEON命令が128ビット幅であるのに対し、AVX2は256ビット幅の演算を行う点が挙げられています。エミュレーション層は256ビット演算を128ビット×2回に分割して処理する必要があり、このオーバーヘッドが性能低下の最大の原因と考えられます。

加えて、PrismのAVX2エミュレーション自体が比較的新しく、SSE系の古い命令セットほど最適化が進んでいない可能性も指摘されました。また、PrismがSnapdragon Xシリーズ向けに特化しているため、Apple M2での性能が最適でない点も影響しているかもしれません。

target_clonesの罠

特に注意が必要なのは、target_clones機能を使っているアプリケーションです。この機能はCPUが対応する最適な命令セットを実行時に自動選択しますが、Windows ARMのエミュレーション環境ではAVX2対応が検出されてしまいます。結果として、ARM搭載PC上で意図せず遅いコードパスが選ばれる可能性があるのです。

開発者が取るべき対策

この結果から導き出される対策は明快です。性能が重要なアプリケーションでは、Windows ARM向けにネイティブARM64バイナリを別途ビルドすることが推奨されます。x86エミュレーションは互換性のためのものであり、性能を期待すべきではありません。

Pythonなどのスクリプト言語を使っている場合も、ARM64版のインタープリタとネイティブホイールがインストールされているか確認する価値があるでしょう。数値計算ライブラリは内部でSIMD命令を活用しているケースが多いため、エミュレーション環境では性能差が顕著になります。

まとめ

Windows ARMのPrismエミュレーションは日常利用では十分な完成度ですが、AVX2レベルの命令セットを使う数値計算では無視できない性能低下が起きることがわかりました。macOS環境でのApple Siliconネイティブ対応が進んだように、Windows ARMでもネイティブビルドの重要性が改めて示された形です。

ベンチマークの詳細はRemObjectsの元記事で確認できます。x86-64の命令セットバージョン定義はWikipediaの解説、Windows ARM上のエミュレーション仕様はMicrosoftの公式ドキュメントが参考になります。