Elixir試飲録 (3) – マルチコア危機によるパラダイムシフト: オブジェクト指向から並行指向へ

マルチコアCPUの登場

ソフトウェアにおける並行性への根本的な転換」。この記事が書かれた2005年というのは、インテルが「Pentium D」「Pentium XE」、AMDが「デュアルコアOpteron 2xx」「Athlon 64 X2」という初めてのマルチコアCPUを発売した年である(参考: ASCII.jp:CPUの勢力図は変わるか? デュアルコアCPU4種対決!)。

それまでムーアの法則に従って指数関数的に向上していた半導体の性能は2003年に転機を迎える。以下のグラフは件の記事で紹介されているもので、CPUのトランジスタ数とクロック数の時系列変化を表している。

Intel CPU introductions (sources: Intel, Wikipedia)

Intel CPU introductions (sources: Intel, Wikipedia)

トランジスタ数は記事の書かれた2005年当時まで、さらには下の2011年までのグラフで分かるように、それ以降もムーアの法則に沿って向上しているが、クロック数の上昇については2003年を境に急激に減速している。クロック数が限界を迎えつつあるのは、それまで以上の高クロック数で動作させたときに発生する「熱」と「消費電力」が許容範囲を超えてしまうようになったからである。

2005年以前、CPUの性能を決定するのは、大きく分けて以下の三つの要因があると言われていた。

  1. クロック数
  2. 命令実行の最適化
  3. キャッシュ

クロック数は単位時間辺りの処理能力に直結する数字で、CPUメーカーは基本的にこの数字を上げることで指数関数的な性能向上を実現してきた。

しかし、クロック数には早晩限界が来ると分かっていたCPUメーカーの開発者は、それでも性能を向上させなければならないというプレッシャーの中、実行対象のプログラムの意味を変えてしまいかねないような大胆な最適化を実装したという。このような最適化を有効にしてプログラムを実行すると、速度自体は向上するものの、プログラマーの期待と異なる動作をする可能性があり、デバッグが困難になるため、通常は無効になっているようだ。

キャッシュの容量を増やすという方法は2005年以降も有効に機能しているようであるが、当然のことながら、それまでのクロック数のように性能を指数関数的に伸ばすというところまでは至らない。

2016年1月現在、インテルから発売されているCPUのクロック数を見てみると、一番高いもので 4.00GHz というのがあるが、これは2005年当時の3.4GHzと比較しても大幅な向上をしているとは言い難く、クロック数についてはほとんど横ばい状態になっていると言って良いだろう。

これまでと同じ方法では同じペースでの性能向上は見込めない。そこで生み出されたのが、マルチコアと呼ばれる、1つのプロセッサ・パッケージ内に複数のプロセッサ・コアを搭載する技術である。これによって、クロック数が頭打ちになっても、CPU全体のトランジスタ数は依然としてムーアの法則に従って向上させて行くことが出来るようになった。

A plot of CPU transistor counts against dates of introduction (sources: Moore's law, Wikipedia)

A plot of CPU transistor counts against dates of introduction (sources: Moore’s law, Wikipedia)

不可避となった並行指向プログラミング

  • マルチコア時代のCPU性能三大要因
    1. ハイパースレッディング
    2. マルチコア
    3. キャッシュ

1つのCPUの性能が頭打ちになったから、複数繋げて1つのCPUに見せかけるというのは、冷静に考えると安直な方法に思えなくもない。実際にインテルの最初のマルチコアCPUである Pentium D は、単純に2つのCPUのダイ (Die) を1つのパッケージに封入したマルチコア・マルチダイ形式だった。

重大な問題は、コアが2つ3つになったからと言って、性能がそのまま2倍3倍になるわけではないということだ。それどころか、それまでのパラダイムで書かれた一般的なプログラムの場合、どう頑張っても一つのコアしか利用することが出来ず、マルチコアの恩恵を受けることが出来ない。

これまではプログラム自体に特に工夫がなくても、時間が経てばCPUの性能が倍々で向上し、あらゆる問題が自然に解決されてきた。しかし、2005年以降、そのようなことを期待することは年々難しくなり、プログラムをマルチコアに合わせて設計し直さない限り、性能向上の恩恵を受けることが出来なくなっている。それどころか、マルチコアの時代においては、一つのコア(一つのスレッド)辺りの性能はむしろ低下する可能性さえあるという。

これまでの逐次的なプログラミングでは年々進歩するCPUの恩恵を受けることは難しくなった。そこで必要になってくるのが、件の記事の主題になっている「並行性」、つまり、マルチスレッドやマルチプロセスを利用したプログラミングである(ここでは、これらを総称して「並行指向プログラミング」と呼ぶことにする)。

今回の記事の著者である Herb Sutter氏は、マルチコア時代における並行指向プログラミングの登場は、1960年代に生まれ、1990年代以降に隆盛を極めたオブジェクト指向プログラミングの登場に匹敵する出来事なのだと主張する。

振り返ってみれば、オブジェクト指向の隆盛はCPUのクロック数が指数関数的に増えて行った時代と丁度重なっており、より複雑でよりリッチなソフトウェアを、よりリッチなハードウェア資源の上で実現するためには最適の手法だったと言えるのかもしれない。

マルチコア時代になって、CPU資源を最大限に活用するために並行性が必須となった。クラウド上で動くプログラムも分散・並行が基本であり、この流れは不可避である。しかし、並行指向プログラミングの最大の問題は「難しい」ということであった。並行処理の難しさは多くのプログラマーによって共有されるところで、安全に並行性を実現するためには、新しいプログラミングモデルが必要になる。そして、ここでオブジェクト指向が足かせになってしまう。オブジェクト指向は処理の前後で意図しない状態変化を引き起こす恐れがあり、安全に並行性を導入するには難がある。そこで注目を浴びているのが副作用のない処理を組み合わせる関数型プログラミングだ。

オブジェクト指向に慣れたプログラマーが並行指向プログラミングに移行するのは、数十年前、構造化プログラミングに慣れたプログラマーが手探りでオブジェクト指向を学んだ時と同じぐらいの試行錯誤があるだろうと、Sutter氏は言う。

今回のネタ帳 – https://oinker.me/room/marubinotto/multicore-crises

Elixir試飲録 (4) – オブジェクト指向と関数型の違い

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中