【S2お茶会】Apple Silicon の世界(1/8)

s2お茶会
ついに僕の元へも clubhouse の招待が来ました。
早速どんな感じなのか体験してみようと思ったのですが、なんだかよくわからなくて何もできずにいます。このままフェードアウトしそうです…。

今回は弊社の栗山が「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までしかつめないというのがいつかネックになるかもしれないなと思ったり。
新製品なのでまだなんとも言えないところはありますが、気になった方は購入を考えてみてください。

今週のお茶菓子