「CPUにはCISC、RISCという設計思想があります」
.........。
だからなんやねん...。
ただ暗記するだけの基本情報技術者試験の知識が本当に嫌いです。「2進数↔10進数変換とか、全く実務に活きないけど得点源だから覚える」みたいな、資格取得が目的化している話を聞いたりすると鳥肌が立ちます。教養としては面白いですが。
一方で、ここからもう少し深堀りしてみると、実は実務で役立つ知識に化けてくる。化けるかも。化けてほしい。ということで、この記事は基本情報で暗記させられるCPUの知識をアプリケーション開発の実務に活かせるものにしていきたいと思います。
設計思想の違いはどうでもいい
まずは簡単なおさらい。
CISC:複雑で多機能な命令を持つ。1命令でメモリ参照込みの処理ができる。命令長は可変。
RISC:単純な命令を組み合わせる。メモリアクセスはload/store命令だけに限定し、計算はレジスタ内で行う。命令長は固定。
これらの設計思想の違いによって、CPUに対して実際の命令を行うバイナリがまるっきり違ってきます。例えば単純なCの実装をサンプルに取ってみます。
// add.c
int add(int a, int b) {
return a + b;
}
これをCISCを採用したx86-64用にコンパイルすると、こんな感じ。
0000000000000000 <add>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 89 7d fc mov %edi,-0x4(%rbp)
b: 89 75 f8 mov %esi,-0x8(%rbp)
e: 8b 55 fc mov -0x4(%rbp),%edx
11: 8b 45 f8 mov -0x8(%rbp),%eax
14: 01 d0 add %edx,%eax
16: 5d pop %rbp
17: c3 ret
一方、RISCを採用したARM64用にコンパイルすると、こうなります。
0000000000000000 <add>:
0: d10043ff sub sp, sp, #0x10
4: b9000fe0 str w0, [sp, #12]
8: b9000be1 str w1, [sp, #8]
c: b9400fe1 ldr w1, [sp, #12]
10: b9400be0 ldr w0, [sp, #8]
14: 0b000020 add w0, w1, w0
18: 910043ff add sp, sp, #0x10
1c: d65f03c0 ret
設計思想が違うことそのものはどうでもいいです。
現実問題として、x86(CISC)用にコンパイルされたバイナリとARM(RISC)用にコンパイルされたバイナリが全然違うために、チップが採用するISA(x86やARMなどの命令セットアーキテクチャ)が合ってないとそもそも動かないということの方が重要です。
Dockerによるバイナリの翻訳
個人的な経験談にはなりますが、AppleがM1チップ(ARM)を採用した当時、Dockerの環境構築に手こずったことを覚えています。自動テスト用のmysqlサーバーをDockerで構築していたのですが、当時社内でたったひとりM1 Macの実験台にされた僕の環境では、動作しませんでした。
mysqlのARM版イメージがまだ存在していなかったためです。上述の通り、バイナリレベルで全然別物なので、M1 Macで動作しません。そこで、プラットフォーム指定をdocker-compose.ymlに追記する形で対応を行いました。
platform: linux/x86_64
これは「このコンテナはx86-64として動かせ」という明示指定です。Dockerは、ARM環境上でx86-64のコンテナを動作させるQEMUというエミュレータを内蔵しており、x86-64で記述されたバイナリをARMバイナリに翻訳することで「M1 Mac上でx86-64コンテナを動かす」ということを実現していたというカラクリです。
当然、QEMUでのバイナリ変換というひと手間(かっこよく言うとオーバーヘッドが)加わるので、処理速度がかなり落ちることがあります。
以下は25年12月のZennの記事ですが、QEMUによる翻訳コストがボトルネックになるケースは今でも発生することがあります。
今はAppleが提供するRosetta 2という翻訳機を使える
OSとCPUを自前で作っているAppleが用意しているRosetta 2をQEMUの代わりとして使えるようになったので、以前よりも高速に動作するようになっているはずです。
また、僕の経験したmysqlサーバーは早い段階でARM版イメージが出ていたので、すでにプラットフォーム指定をせずに純粋なARMで動作させることができるようになっています。
一方、開発が止まってしまっていてARM版が存在しない&開発予定もないものは、今もplatform指定を行ってRosetta 2でエミュレートしながら動かす他ありません。
最近のトレンドはARM
RISCを採用した設計について、現場目線で何が一番いいのかというと、命令セットがシンプル=消費電力がCISCに比べて少ない=基本的にコスパ良いという点です。
消費電力を極力抑えたいスマホ向けチップではARMが基本的に採用されていますし、クラウドインフラでももはやARMが主流になりつつあるんじゃないかと思うくらいコスパが良いです。
例えば、AmazonはGravitonというARMの独自チップを作っていて、処理速度とコスパの良さを売りにしています。
まず、Graviton2 アーキテクチャにより、関数がより効率的に実行されます。第二に、実行する時間に対する支払いが少なくなります。実際、Graviton2 を搭載した Lambda 関数は、20% 低いコストで最大 19% 優れたパフォーマンスを実現するように設計されています。
僕自身も、IntelチップよりGravitonの方が処理速度も早いしコストも低いしで、新しく作る環境はARMにします。
まとめ
基本情報の「CISC/RISC」をただ暗記するのはつまらないですが、その背後にある一点(アーキテクチャが違えばバイナリは別物)は、クラウドとコンテナを開発者自身が触る今の時代、地味に毎回効いてきます。
- x86-64とARM64は命令セットが違う別言語。コンパイル済みバイナリはそのままでは動かない
- Rosetta 2もDockerのQEMUも、x86バイナリをARMバイナリに翻訳して橋渡ししている
- 翻訳にはコストがかかるので、当然遅くなる
- ARMが流行るのは電力あたりの性能が高く、結果としてコスパが良いから
ここまで掘ってみると、Dockerでの仮想環境構築やクラウドインフラの設計という実務に「CISC/RISC」という基本情報の知識が接続された気がしなくもないですね。
では、ここまでお付き合いいただきありがとうございました。
今もはや純粋に「CISC/RISC」という二元論でCPU設計を語れないというか、CISCでもRISC的な設計を取り入れていたり、RISCでもCISCの良い点を取り入れていたりするそうなので、基本情報の知識だけでは乗り遅れている感も否めないです...笑。オブジェクト指向 vs 関数型と似たような古さを感じます。
