Apple Silicon の世界
早速どんな感じなのか体験してみようと思ったのですが、なんだかよくわからなくて何もできずにいます。このままフェードアウトしそうです…。
今回は弊社の栗山が「Apple Silicon」について話しました。
Apple Silicon とは Apple が ARM アーキテクチャを使って設計したチップ(SoC)・パッケージ(SiP)プロセッサの総称です。
昨年の11月に初めて Apple M1 プロセッサを使用した Mac が発表され話題になりましたが、Apple Silicon 自体はこれまでも iPhone や iPad、iPod Touch などの様々な製品に用いられてきました。
現時点で 7 種のシリーズがあり、今回 Mac 用として新たに加わったのがMシリーズになります。
- Aシリーズ … iPhone, iPad
- Sシリーズ … Apple Watch
- Tシリーズ … Mac 向けの補助プロセッサ(Touch ID やエンコーディング)
- W・Hシリーズ … AirPods などのワイヤレス関連
- Mシリーズ … Mac 用
- Uシリーズ … iPhone, Watch の UWB 用
ARM アーキテクチャとは
簡単に説明すると、イギリスに本社のあるARMホールディングスが開発した命令セットのことで、
どのバイトコードがどういう働きをするかを定義したものです。
現状、コンシューマー向けに普及している CPU のアーキテクチャは大まかに Intel と ARM しか残っていません。
Intel はアーキテクチャの設計・実装・販売、さらに製造までを自社で行うというスタイルなのに対し、ARM は設計したものをライセンスして、実装や販売は他社に任せるスタイルを取っています。
Android の Snapdragon (Qualcomm) 、Nintendo Switch の Tegra (NVIDIA) 、Raspberry Pi なども ARM が使われています。
ARM は昔から携帯系の低消費電力のものにめっぽう強く、その辺りから普及し始めて今度はノートパソコンやデスクトップパソコンにも手を広げてきたといった様子。
Intel は、より性能が高く処理の速いものをつくって売るという戦略でこれまでやってきたのですが、より低消費電力ながら処理の速いものをつくるという ARM の戦略によって段々と差を縮められ、今に至るといった感じです。
ARM と Intel の違い
ARM には RISC、Intel には CISC という命令セットアーキテクチャの設計の方向性があり、その違いが一番にあげられるでしょう。
個人的主観で大雑把に言うと、RISC ではコンパイラが頑張って物理 CPU はシンプルにしてスループットを上げる。対して CISC は物理 CPU が頑張って性能を上げ、一つの命令でできることを増やす。
という設計方針のように思います。
とはいえ、Intel も ARM もお互いの良いところを取り入れつつあるので、厳密に分かれるものでもなくなってきているように感じます。
Apple M1
今まで MacOS には長らく Intel が採用されてきましたが、Intel に代わり ARM アーキテクチャを使って設計・製造されたのが、最初に話した Apple M1 というCPUです。従来、ARM CPU は Intel CPU よりもピーク性能で劣る(電力効率は ARM の方が良い)ことが多かったのですが、意外と性能が良かったので世間では驚かれているとのこと。
CPU が変わるとアプリケーションが熟れてくるまで、もしくはプログラマーがそれに対応して最適なコードを書けるようになるまで、性能が存分に発揮されなかったり、すぐに落ちたりバグが出たりということが多かったように思うのですが、私自身 Apple M1 が搭載された Mac を使っていてそんなに動かないものが多いわけではないし、性能的にもむしろ良いところが多いと実感しています。
ベースとなるのは iPad 用の A14 Bionic で、他 ARM CPU でも見られる big.LITTLE 構成です。
- 高性能コア「Firestorm」… よりスピードが出る CPU コア
- 高効率コア「Icestorm」 … 速度は遅いが消費電力が少なくて済む CPU コア
が各 4 つずつ使われており、これによって、電力を消費して性能を出すシーンと、そこそこの性能を低消費電力でこなすシーンを使い分けられるようになっています。
iPad では必要のなかった外部インターフェイス用のロジックや高性能メモリコントローラーなども組み込まれているそうです。
Intel の CPU は Windows や MS-DOS までが動作するように互換性を考慮した作りになっているため、ほとんど今では必要ないのに入れておかなければいけないものが多く、そういう面も含めて効率的に不利ですが、ARM は互換性をあまり考えず、今必要なロジックだけを CPU に入れればいい作りになっているので、その分コンパクトになり、結果的に消費電力も下げられました。
メモリも高速なものを、より広いバスを使い、かつ近距離に配置することで、パフォーマンスが上がるようにしています。
また、今後はわかりませんが、製造する SKU を絞って生産効率が上がるようにもしているみたいです。
LLVM / Clang / Xcode
macOS 上で動作するアプリケーションは Xcode でビルドされるのですが、その中身は LLVM / Clang で、そこから Apple Silicon ネイティブなバイナリが生成できます。通常の Swift で記述してある従来コードも、よりハードウェアに密接なコードとかでなければ、それほど手間はかからず再コンパイルするだけでApple Silicon ネイティブなバイナリが生成されるはずです。
ただ、 Apple Silicon 特有の対応が必要なせいだと思いますが、まだ移行できていないアプリもあります。具体的にどの辺りがネックになるのかは不明です。
Unix 系の CLI で使うようなツールも LLVM / Clang でコンパイルされます。
大きいところでは Go 言語が Apple Silicon 対応できずにいたのですが、現在では改善され on-commit ビルドにも加えられており、2 月の 1.16 リリースでサポート対象になる予定で作業が進んでいます。これによって Go に依存しているアプリも Apple Silicon ネイティブバイナリが普及するはずです。
オープンソース界隈では「ARM だからコンパイルできない」というケースはあまりなく、macOS 特有のハードウェアやデバイス、OS API がらみで詰まってることが多い印象。
Sophos などは M1 以前に Big Sur で動かないこともあったので、同時期に出てくるとどっちが原因なのかわからないケースも。
Intel バイナリ / Rosetta 2 / Universal 2
ネイティブバイナリが提供されないソフトウェアについては、Rosetta 2 という仕組みで Intel バイナリを Apple Silicon バイナリに変換した上で実行できることが多いです。Intel インストラクションセットと ARM インストラクションセットが一対一で対応するわけではないので、どれも完璧には変換できませんが、観測している限り動くものはそこそこちゃんと動くし、性能も問題ないレベルでした。
最初に起動した時に変換処理が入るので、バイナリの大きさに依存しますが、少々待たされる場合もあるようです。
変換後はネイティブバイナリになるので、実行性能はかなり良好ですが、Intel 向けバイナリには Intel CPU の挙動に極度に依存したチューニングが行われているものもあり、単純に命令通り ARM の方に変換すると想定外の動きをしてしまうことがあります。そういう時には保守的に安全な形に変換するなどしてくれるのですが、その分 Intel での動作よりは遅くなるケースもあるそうです。
元々、 macOS には Universal Binary という、複数のアーキテクチャのネイティブバイナリを一つのアプリケーションに入れることのできる仕組みがあり、今回も多くの両対応アプリケーションはこの形で提供されています。
Google Chrome などは、バイナリ自体が大きいため、両方入れるとダウンロード時の無駄が大きいとのことで、とりあえずそれぞれどちらのアーキテクチャを使うのかをダウンロード時に選ばせるようにしているみたいです。
Docker
12月中旬に Apple Silicon 対応版が出ました。Docker Desktop はいくつかのプロセスから成り立っていますが、Apple Silicon ネイティブで動いているのはそのうちの半分ぐらい。コアな部分は対応しているようなのですが、ネットワーク系やファイルシステムをマウントするあたりのところには Intel バイナリのプロセスも一部残っている様子です。
CPU 関連だけですが速度を比べてみました。
MacBook Pro (16-inch 2019, Intel Core i9 2.4GHz x 8)
$ openssl speed -evp aes-128-ctr
...
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes
aes-128-ctr 1036087.71k 3063056.47k 5256430.93k 5877817.10k 6848586.31k
$ docker run --rm -it --platform linux/amd64 alpine sh -c "apk add -q --no-cache openssl; openssl speed -evp aes-128-ctr"
...
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
aes-128-ctr 589614.89k 1935324.18k 3940432.81k 5312362.84k 5881083.38k 5884280.83k
$ docker run --rm -it --platform linux/arm64 alpine sh -c "apk add -q --no-cache openssl; openssl speed -evp aes-128-ctr"
...
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
aes-128-ctr 15681.37k 31170.84k 43440.46k 47137.11k 49591.64k 49971.20k
MacBook Pro (13-inch 2020, Apple M1)
$ openssl speed -evp aes-128-ctr
...
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes
aes-128-ctr 343671.25k 370727.70k 377603.82k 379880.68k 379919.84k
$ docker run --rm -it --platform linux/amd64 alpine sh -c "apk add -q --no-cache openssl; openssl speed -evp aes-128-ctr"
...
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
aes-128-ctr 42644.98k 62578.54k 71621.97k 74268.33k 75025.07k 75180.71k
$ docker run --rm -it --platform linux/arm64 alpine sh -c "apk add -q --no-cache openssl; openssl speed -evp aes-128-ctr"
...
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
aes-128-ctr 938419.47k 3120387.97k 7109979.56k 9770274.13k 10910706.35k 11016246.61k
1024bytes
の列だけ見ていただいて、両方とも上から順に
- MacBook Pro のターミナル上で CPU のスピードを計測した時
- それぞれの CPU のDocker で Intel 版の Docker を実行した時
- それぞれの CPU のDocker で ARM 版の Docker を実行した時
です。
上の MacBook Pro はCatalina, 下は Big Sur で、バージョンが異なるせいもあるのかもしれませんが、M1の ARM CPU の Docker で ARM 版の Docker を実行した時の速度が Intel の CPUで実行した時のどの速度よりも圧倒的に速いのがわかると思います。
Docker のイメージの仕組みはマルチアーキテクチャに対応しているのですが、メジャーな公式イメージは linux/amd64
に加えて linux/arm64
も Docker Hub にアップロードされているものの、マイナーなイメージは amd64
だけしか存在しません。
今後、Docker を使って開発をするスタッフが増えてくるような場合は、それに対応する形でイメージを用意しなければいけないなと思いました。
実際、M1 の MacBookPro を使ってみていて、Intel のものよりもバッテリーの減りは少なく感じます。
性能にも今のところ不満はありませんが、これから仕事でどんどん使っていくうちに、メモリが16Gまでしかつめないというのがいつかネックになるかもしれないなと思ったり。
新製品なのでまだなんとも言えないところはありますが、気になった方は購入を考えてみてください。