ここしばらく、色々なしがらみもあって MIMD メニーコアのシステムについて
色々みたり聞いたり文句をいったりする機会が多いのですが、連続的な進化、
というのはやはり限界に近づいている、というか、既に限界を超えているのではと
いうふうに思います。以下、なにが問題か、対応策はあるのか、というのを
少し議論してみます。
将来というか、まず現在の困難というのは Xeon Phi に表現されています。
但し、KNF とか KNC では、あまりに設計がアレであるという問題が多す
ぎて、MIMD メニーコアの本質的な問題点が見えにくくなっています。
まず設計上の問題点です。
主記憶のアクセスレイテンシが 250サイクルくらい
(らしい)のはまあしょうがないとして、リモートキャッシュも同じくらいある
とか、なにがどうなってるかよくわからないけどバリア同期に10マイクロ秒以
上かかるとかいうことが実測から明らかになっています。
論文は
これ
です。
バリア同期に 10 マイクロ秒というのは全くお話にならない数字です。という
のは、これは、チップ内並列化の最小単位の実行時間が100マイクロ秒くらいな
いとまともな性能がでない、ということを意味するからです。KNC は名目ピー
クで 1TFくらいなので、ある程度の実行効率がでているなら同期の間に1億演算
くらいないと同期オーバーヘッドによる性能低下が無視できなくなるわけです。
しかし、L2キャッシュはチップ全体で30MBくらいしかありません。倍精度だと
概ね 4M語です。L21語アクセスにつき1演算とすると、25回L2全体をなめる毎に
1度だけ同期する、みたいなプログラムである必要があります。また、多体問題
の例だと、100万相互作用くらい計算してから同期、という程度まで同期の回数
を減らす必要があります。こうなると、ループ単位で OpenMP でプラグマいれ
て、というアプローチでは駄目で、プログラム全体をまずマルチスレッド化し
て、どうしても必要なところだけで同期する、としないといけないわけです。
「京」は(というか「京」の Venus とかその前の Jupiter から)、ハードウェ
アバリアの機構がついてるのでこんな阿呆なことはありません。マイクロ秒以
下で同期が可能です。これは富士通のアプローチの明らかに優れているところ
です。「京」はさらに L2が共有なので、OpenMP による並列化には非常に具合
がよくなっています。
但し、では近未来、というか Xeon Phi の現在である 64 コア程度で共有L2 が
もてるかというともちろん無理で、FX100 (SPARC64 XIfx) では32コアを 16 コ
ア毎の2ブロックにわけています。これもかなりの力技で、「京」に比べると
L2 のサイクルあたりのデータ供給量が 4 倍(コア数2倍、SIMD幅2倍)です。つ
まり、4倍の本数の配線がいるわけです。また、共有 L2 ではチップ全体に対し
て物理的な L2 の数が少なく、必然的にL2 とプロセッサコアの間の配線はチッ
プサイズ程度の長さになります。これは半導体テクノロジーにあまり関係なく
長い配線が発生する、ということです。。そうすると、データ移動に必要なエ
ネルギー消費が半導体テクノロジーが進歩してもあまり減らないことになりま
す。
実際には線を細くし、バッファを増やし、パイプラインステージ数を増やすと
かしているはずです。そうすると、必要エネルギーはある程度減らせるが、L2
アクセスレイテンシがどんどん増えることになります。これは何を意味するか
というと、1つの L2が供給できるバンド幅(サイクルあたり)には物理的な限界
があるということです。「京」の、名目B/F=2 でFMA32個、128B/サイクルとい
うあたりが配線可能な限界に近く(B/F 2であってたかどうか確認必要)、FX100
は「京」の4倍必要で、かなり無理をしているわけです。
これは、80年代後半から90年代前半、つまり25年前に共有メモリ型ベクトルプ
ロセッサで起こったことの繰り返しです。16プロセッサ2パイプの Cray C90 が
B/F=4 なら 16*4*4=256B/サイクルで、Cray の場合にはその次の T90 で会社が
なくなりました。NEC は32プロセッサの の SX-7が最大で、これは は2KB/サイ
クルと、すさまじいアクセス幅のメモリをもっています。それ以降は共有メモ
リにつながるプロセッサ数を減らしています。
つまり、配線可能にするには、多数のコアによるL2共有を諦めるしかありませ
ん。もちろん、Xeon Phi はその方向になっていて、60コアくらいの L2 が論理
的にはリングでつながっています。これは、90年代中頃の、分散共有メモリマ
シン Stanford DASH を1チップにしたようなものです。DASH は、基本的に
64台のRISCプロセッサワークステーションをメッシュネットワークでつないだ
ものですが、そのネットワーク上でディレクトリベースのキャッシュコヒーレ
ンシプロトコルが走り、共有メモリモデルのアプリケーションプログラムが
(理屈としては)動作します。
なお、現在のところ、Xeon Phi では主記憶であるオンボードのメモリは物理的に共有
されていますが、L2キャッシュはコア間で分散する、という形です。これに対して
DASH は主記憶ももちろん分散なので、そこは大きく違っています。
しかし、コア数や外部メモリへのバンド幅がさらに増えると、結局
全コアから全外部メモリがユニフォームにアクセス、というのも無理になるわ
けで、DASH のような分散メモリに近づいていきます。
つまり、コアに物理的に近いか遠いかで主記憶のレイテンシやバンド幅が変わる、1ソ
ケットNUMA アーキテクチャになるわけです。実際のところ Xeon Phi では
複数のメモリコントローラが「リング」につながっているので、全てのコアが
同じレイテンシでアクセスできるわけではありません。その意味では、既に
NUMA になっています。
(3/22、上のパラグラフを、xmmymmzmm さんのコメントをうけて修正しました)
NUMAは程度問題で、それに適応しないと性能がでない、となるとアプリ
ケーションが書けなくなって死んでしまいます。結局、PGAS と同様に明示的に
データの置き場所を指定し、さらに演算をする場所も指定しないと性能がでな
いのです。
ここまでの話をまとめると、すでに何度か書いた気もしますが、単に共有メモ
リは 16コア程度が限界、という話です。これが巨大なベクトルプロセッサであ
ろうと、1チップ1コアのスカラープロセッサであろうと、マルチコアプロセッ
サであろうと、あまり変わりません。物理的な共有メモリのためにはクロスバー
スイッチが必要であり、またキャッシュコヒーレントにするためには
単純にはすべてのコアが他の全てのコアのことを知っていないといけないわけ
でコア数の2乗でコア間転送データが増えます。
コア内の SIMD 幅を増やし、キャッシュのラインサイズを増やせば、相対的に
コヒーレンシ維持のためのコア間通信のデータ転送量は小さくなりますが、
共有メモリ自体へのクロスバースイッチのサイズはデータ幅に比例以上に
大きくなるので、より問題が深刻になります。とはいえ、
コア数とSIMD幅を同じ程度にしておくのは、おそらく面積や消費電力の
観点から最適に近いものになっていると考えられます。
と、ここまではコヒーレンシプロトコルによる分散キャッシュの話です。
コヒーレンシを放棄すれば話は簡単になります。また、アプリケーション
プログラム側の観点からは、コヒーレンシがあるキャッシュというのは、いつ
も必要、というわけでは決してありません。
例えば、メッシュ法、陽解法での流体計算を考えます。コア毎に分担する格子
を決めたとすると、実際に各点のための計算をしている間は、キャッシュコヒー
レンシは全く不要です。それぞれが独立に計算を進められるからです。もちろ
ん、細かいことをいうと、1つのキャッシュラインにあるデータを複数のコアで
分担してしまったりすれば、その時にはややこしい問題が発生します。が、こ
れはあまり本質的なことではなく、本質的には主記憶に更新したデータを書いてから
次のステップに進む前に同期すれば十分です。
では、キャッシュコヒーレンシはやめて分散メモリにしたらどうでしょうか?
Adapteva の
Epiphany
はそもそもキャッシュではなくプロセッサコア毎にローカルメモリをもつよう
になっています。他のプロセッサのメモリはルータ経由ですがグローバルアド
レス空間からアクセスできます。PGAS的ですね。
この場合、既存のプログラムを OpenMP でなんとか、というわけにはいきませ
ん。また、ローカルメモリ空間とグローバルメモリ空間を明示的にプログラム
の中で使い分ける必要があります。要するに、「共有メモリ」ではなく、
ルータがリモートメモリアクセスをサポートする Cray XC30 が1チップになっ
たようなものです。
PEZY SC は、コヒーレンシのない階層キャッシュ、という、アプリケーション
を書く側から見るともっとも筋の通った構成で、原理的にはグローバルメモリ
のシリアルコードから、並列化、キャッシュ再利用を促進する最適化、と
いう順序で、コードを少しづつ改良していくアプローチが可能なはずです。
Adapteva の Epiphany にしても、PEZY-SC にしても、コア内では基本的に
SIMD にせず、小さなコアを多数集積しています。実際の電力性能やチップサ
イズをみる限り、このアプローチのほうが、Intel 等のとっている、
コア数と SIMD幅を同程度にする、というアプローチよりも良い結果を
だしています。もちろん、これはキャッシュコヒーレンシをとらないので、
コア間通信がコア数に比例する程度に抑えられる(と期待する)からです。
では、この方向で将来万歳か、というと、実は「プログラムメモリをどうする
か」という問題がどうしても発生します。L1命令キャッシュがある程度の大き
さがないと、最内側ループがそもそもはいらないため分割しないといけない、
といった問題が生じます。また、分割までいかなくても、アンロールできない
のでパイプラインが埋まらないという種類の問題も発生します。ハードウェア
で時分割型のマルチスレッドをサポートして、複数の命令列が並行して動くよ
うにすれば、分岐のペナルティやメモリアクセスのレイテンシを隠蔽はできま
すが、そのかわりレジスタファイルが大規模化します。また、L1 が小さい、と
いう問題は残っているので、折角マルチスレッド化しているんだけど、実際問
題としては全スレッドで同じループを実行していないと性能がでません。
また、階層キャッシュには階層キャッシュの問題があります。
結局通信はすべ
て物理的に共有されているメモリ階層を通じた、ある意味非明示的なものにな
るので、通信のために実際に発生するコストがかなり高くなるのです。
アプリケーションレベルでは、コアAのこのデータをコアBのあそこに
書きたい、とわかっていても、書き込みは共有メモリへとなり相手のキャッシュ
に直接書く方法自体が存在しないので、まずコアAが、コアBと共有している
メモリ階層までキャッシュをフラッシュし、コアAとBを含むプロセッサグルー
プで同期し、それからコアBがメモリを読む、ということによって通信が起こ
るわけですから、書き込みレイテンシ、同期コスト、読出しレイテンシが
見え、しかもこれらは隠蔽できない(書き込みと読出しは同期によって時間方
向に分離されている)わけです。
これを回避するには Ephiphany のようなルーティングネットワー
クを別につければいいわけですが、そうすると段々何をしているのか
わからなくなってきます。
では、ということでコアを大きくし、コア内 SIMD 幅を広くすれば、コア数が
減るので色々な問題は若干緩和されます。特に、命令L1 を大きくすることはで
きます。しかし、これは、SIMD 命令の利用効率が低い、という現在おこってい
る問題を解決しないと実用的には使いものになりません。
コア内 SIMDの問題については何度か書いたので繰り返しませんが、
要するに、実行可能な操作、特にメモリからのロード・ストアが、
Cray-1 や Cyber 205 の時代のベクトル機レベルであることが本質的です。
SIMD 幅があるので、メモリアクセスも1サイクルに複数語するわけですが、
これがランダムアクセスできない(するとスループットが著しく落ちる)
ことが問題です。Cray では XMP で gather/scatter をサポートし、
この問題をほぼ解決しました。Cyber は一応つけましたが、連続アクセスの場
合に比べてスループットが 1/3 程度になる、という問題は解決できませんで
した。
コア内 SIMD 幅が大きくなければ、この問題はあまり深刻化しません。
その意味で、「京」の、幅2のSIMDユニットが2つ、というのは
良い選択であり、ロード・ストアが連続アドレスではない場合でも極端な性能
低下はしませんでした。しかし、SIMD幅が8となるとこれはどうにもならなく
なります。
もちろん、キャッシュをもっているので、そもそも主記憶にランダムアクセス
をするとキャッシュラインのほとんどが無駄になる、という問題はあり、
アプリケーション側で「ランダム」といってもキャッシュ内になるべくはいる
ような並べかえができないとそもそも絶望的ですが、非構造格子のような
問題でれば、物理的に存在する空間局所性をアドレス局所性に反映させること
は常に可能です。従って、主記憶に対する高いランダムアクセス性能はまあ
無理として、L2 キャッシュくらいに対してランダムアクセス可能ならまあ
だいぶましでしょう。
このためにはL2を多バンク化してマルチパイプのベクトルプロセッサみたいなこ
とをすればいいわけです。要するに、NEC SX-4 から6辺りの1コアを
もってくればいい。さらに、マルチスレッドを止めてベクトル命令にしてしま
えばL1へのプレッシャーも減るし、そもそも命令デコードのコストも小さく
なってよい、と私は思います。
整理すると、キャッシュコヒーレンシを気にしなければ、コア数を増やしても
一応大丈夫なのですが、命令供給をどうするかという問題は発生する。これを
回避するためには結局コア内並列度をあげてコア数を減らすわけですが、
L2 を多バンク化しない現在のアプローチは既に破綻していて、多バンク化
してベクトルプロセッサみたいなものをコアにしても、演算器16-32でやはり破
綻します。まあ、16個くらい演算器があれば、128KBくらいの命令メモリがあっ
てもいけないことはないので、プログラムを載せるのはだいぶ楽にはなります。
が、演算器16個に対してマルチバンクのL2が現実的なコストでできるかどうか
はかなり微妙です。その間をつなぐクロスバーが複雑化するからです。
SIMD であってもマルチバンクにしないで、演算器毎に
ローカルメモリにすれば別にそれ以上マルチバンクにする必要はありません。
これは、コア内でさらにアドレス空間を分割するというなんだかよくわからな
いことになりますが、これをやったのが
要するに我々のGRAPE-DR/GRAPE-X/PACS-Gです。これはSIMD化を極限まで
進めて、1チップ全体が SIMD動作します。演算器毎にローカルメモリがあり、
その中は演算器毎に別のポインタでランダムアクセスできます。ある程度局所
化された範囲でのランダムアクセスであればこれで十分です。
複数の、ローカルメモリをもつコアにまたがって SIMD 化することには
結構沢山メリットがあります。
-
命令ストアに必要なメモリ量、読出しに必要なバンド幅要求がSIMD幅に
反比例して小さくなる
-
同期やキャッシュフラッシュに時間がかからない。SIMDなので常に同期しているから
-
同期コストが低いため、コアにまたがった総和等の縮約演算も低いオーバー
ヘッドで実行できる
言い換えると、MIMD メニーコアには、
-
コア数に比例して命令ストアに必要なメモリ量が増える、
-
同期やキャッシュフラッシュに大きなコストがかかる
-
コヒーレントキャッシュでは、そもそもコア数の2乗に
比例して通信量が増加する。ハードウェア量はもっと増える
-
コア内 SIMD 幅には、「共有メモリである」ことからくる物理的限界がある。
-
コア内 SIMDは、現在の、ランダムアクセスできないものは性能向上に役に立たない
-
コア内 SIMDは、ランダムアクセスできるようにするのはハードウェアコストが高過ぎる
-
同期コストが高いため、コアにまたがった縮約等が困難である
といった問題があるわけです。こんなことは少なくとも25年前からわかりきっ
ていると思うわけですが、まともな解決策と思えるものが提案されていないの
は不思議なことです。個人的には、どこかで分散メモリ SIMD メニーコアにい
くしかないと思います。