![見出し画像](https://assets.st-note.com/production/uploads/images/39691714/rectangle_large_type_2_e1da7c7cbc2b8f17a64dd673cba9dbd8.jpeg?width=800)
M1でやってるらしい(Apple用の)最適化
Apple M1、命令実行数とかL1キャッシュどうなってんのとかはAnandTechが詳しいと思う。
Apple M1速いね、ってことで、それはいいとして、それ以外にも色々Appleの用途に最適化している点があるらしいというツイートがあった。ちょっと読んでてマジで?となったのでここにメモしておこう。
私はというとこんなCPUレベルの話が効いてくるようなプログラムは書いたことないので、誤解もあると思う。ゆるして
1/ In case you were wondering: Apple's replacement for Intel processors turns out to work really, really well. Some otherwise skeptical techies are calling it "black magic". It runs Intel code extraordinarily well.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 25, 2020
BlackICEを作ったとか書いてあるからセキュリティソフト関連の人なのかな?Robert Grahamという人の…このスレッドが興味深かった。
メモリオーダリング
2/ The basic reason is that Arm and Intel architectures have converged. Yes, the instruction sets are different, but the underlying architectural issues have become very similar.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 25, 2020
3/ The biggest hurdle was "memory-ordering", the order in which two CPUs see modifications in memory by each other. It's the biggest problem affecting Microsoft's emulation of x86 on their Arm-based "Surface" laptops.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 25, 2020
この方の指摘するところによれば、Armでx86のコードをエミュレーションする場合、問題となるのは命令セットそのものではなく"memory-ordering"なのだと言う。私はよく知らなかったのでバイトオーダーのことかと思ったら違った。メモリオーダリングというのは、CPUの命令実行順序の並び替えをどの程度許容するか、ということなのだそうだ。
つまり私がプログラムを書いて、それをコンパイルしてCPUが実行するとき、命令の実行順序というのは元のプログラム通りにはならない。同時に実行しても問題ない命令は同時に実行したり、こっちの命令で読み込むのに時間がかかるからその間にあっちの命令を実行しよう、とか効率の良いように並び替える。ここで、何の制限もなしに命令を並び替えると当然実行結果が変わっちゃうので、こういう場合は並び替えない、といった制限をかける。この制限がArmとx86で言うとArmの方がより「緩い(weak)」。よりホイホイ命令を並び替えちゃうので、それが問題になる場合はソフトウェアの方でなんとかしろ派。
ということは、もしx86のある命令をArmの命令に一対一で変換できたとしても、それをArmが実行するときにx86では想定していない並び替えが起こる可能性がある。これじゃ実行結果が変わってしまうので、命令を変換するときにはそこを考慮して変換するひと手間がかかり、その分遅くなるのだろう。じゃあAppleのRosetta 2はどうしたのか?
4/ So Apple simply cheated. They added Intel's memory-ordering to their CPU. When running translated x86 code, they switch the mode of the CPU to conform to Intel's memory ordering.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 25, 2020
この方の言うところによればAppleはx86のコードを変換した時用のモードをM1に追加しているらしい。マジで?このモードであれば、x86のある命令をArmの命令に一対一で変換できれば、x86で想定した範囲の並び替えしか起こらない。シンプル。
5/ With underlying architectural issues ironed out, running x86 code simply means translating those instructions to the Arm equivalent. This is very efficient and results in code that often runs at the same speed.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 25, 2020
6/ Sometimes there isn't a direct equivalent, so the translation results in slightly slower code, but benchmarks show x86 being consistently at least 70% of the speed.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 25, 2020
実際には命令が一対一対応しているわけではないので変換はゼロコストではないにせよ、オーバーヘッドが30%というのは確かに十分速い。
JavaScriptへの最適化
9/ Apple has made surprising choices. They've optimized JavaScript, with special JavaScript-specific instructions, double sized L1 caches, and probably other tricks I don't know of.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 25, 2020
私には確かめようがないんだけど、JavaScriptを効率的に実行するための命令の追加、L1キャッシュの増加、などなどをしていると言う。マジで?JavaScriptってインタプリタ言語でしょ?と思うけど現代ではJITコンパイルで実行されるし、そこに効く最適化というのもあるのかな…AppleはWebKitも作ってるわけだから、WebKitでどこがボトルネックになっているかも分かるはず。
M1の発表イベントではBig Surとの組み合わせでJavaScriptの実行が1.5倍速くなったと言っていたので、何らかの最適化があったのは確かなのだろう。
参照カウント
iOSおよびMacのソフトウェア開発に使われる言語、Objective-CとSwiftでは、一般的なガベージコレクションによるメモリ管理を採用していない(正確にはMacで一時採用したが、即辞めた)。どうしているのかというと、参照カウントを使う。
つまり、aという変数に何かオブジェクトを代入すると、そのオブジェクトにくっついているカウンタを+1する。aにnullとか他のオブジェクトを代入すると、カウンタは-1される。こうして、どこからも参照されていないオブジェクトはカウンタが0になると解放される。コードを書くとき循環参照に注意する必要があるけれど、「ガベージコレクタが動く時間」というものがない。必要なくなったオブジェクトは即解放されるので。
その代わり、参照しただけでこのカウンタを増やしたり減らしたりする処理が挟まる。アプリ全体ではこの部分だけでも馬鹿にならない回数あるはずだし、これはスレッドセーフでないといけない。
18/ Another "magic" trick is how their "Swift" programming language uses "reference counting" instead of the "garbage collection" in Android. They did something in their CPU to double the speed of reference counting.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 26, 2020
19/ ...even when translating x86 code, all that reference counting overhead (already more efficient than garbage collection) gets dropped in half. Yet another weird performance enhance to add to all the others.
— Robᵉʳᵗ Graham😷, provocateur (@ErrataRob) November 26, 2020
ということで、AppleはM1採用によってこのカウンタ処理を最適化したらしい。どれくらい速くなったのかはAppleのエンジニアもツイートしている。
fun fact: retaining and releasing an NSObject takes ~30 nanoseconds on current gen Intel, and ~6.5 nanoseconds on an M1
— David Smith (@Catfish_Man) November 10, 2020
…and ~14 nanoseconds on an M1 emulating an Intel 😇
— David Smith (@Catfish_Man) November 10, 2020
エッ、マジで?さっき言った参照カウントの増減にIntelでは最大30 nsかかっていたところをM1では最大6.5 ns、Rosetta 2で実行時でさえ最大14 nsなのだという。これはCやC++でゴリゴリ書いた処理のところではなくて、Objective-CやSwiftで書く部分特有のことなので、おそらく世のベンチマークソフトの数字には現れないけれど、アプリのUI部分では馬鹿にならないはず。
Weaker memory model makes acquire-release atomics possible to implement much more efficiently, in exchange for not hiding some classes of multithreading bugs
— David Smith (@Catfish_Man) November 10, 2020
(The price we pay for this is that certain kinds of multithreading bugs can remain dormant on Intel but become symptomatic on M1. Use Thread Sanitizer to test your apps!)
— David Smith (@Catfish_Man) November 10, 2020
どうやって最適化したのか、というとさっきのメモリオーダリングの話らしい。参照カウントの整合性をとりつつ速度を出せる程度にArmは「緩い」。一方でIntelはこの用途には不必要なくらい「厳しい」のだが、そのおかげで顕在化しなかったバグがArmでは出てきてしまう場合があるらしい。
まあこれはiOSでは元々そうだったんじゃないかな…
64bit移行
MacがCPUを代えるのはこれで3度めと言われている。68K → PowerPC → Intel → Arm。でも細かいことを言えば、同じアーキテクチャの中でも命令セットは変わっていくし、64bit移行というでかい変更があった。IntelでもArmでも、32bit命令セットと64bit命令セットというのは異なる命令セットだが、64bitプロセッサでは32bit命令も実行できるようにして互換性を保ってきた。
ところが、慈愛に満ちたMicrosoft Windowsのお世話になっている人には信じられないかもしれないが、MacのArm移行前に、既にMac (Intel) とiOS (Arm)は32bitのアプリは切り捨て、64bitのみサポートしている状態になっていた。つまり、M1 + Rosetta 2のエミュレーションは64bit (x86-64 → AArch64)だけサポートすればよく、32bit (IA-32)のことは考えなくていい。
MicrosoftのArm版Windowsが最初IA-32エミュレーションのみ対応しててx86-64はこれから、というのと比べると手間が半分になるわけだ。ずっこい。
さらに言えば、MacもiOSももう32bitコードをサポートしないので…Apple SiliconからはAArch32も切っちゃっていいのでは?Intelのプロセッサは汎用なので、これからもしばらくは互換性のために32bit命令をサポートするだろう。でもAppleは自社のApple Siliconから32bit対応を即座に切れる。だって自社のOS用に作っていて、そのOSは既に32bit対応を切ってるから。ずっこい!
Appleのあんこくどくさい体制ずっこいねという話よ…
この記事が気に入ったらサポートをしてみませんか?