GPGPUの特徴

GPGPUについて調べたことのメモ。


GPGPUとは、General Purpose Computing on GPUの略。これまでグラフィック処理専用に使われていたGPUビデオカードに乗ってるプロセッサ)を、もっと汎用的に使おう、という考え。GPUは並列処理に特化した構成になっているため、プログラムによってはCPUより高速に演算することができる。


GPGPUでは基本的に、並列処理に特化したプロセッサーGPU)を汎用プロセッサー(CPU)で制御する、という構成になっている。GPUはマルチコアになっていて、内部に多数の演算ユニット(スレッド)を備えている。各スレッドがそれぞれに処理を実行することで、並列処理を実現している。各スレッドはまったく別々の処理を行うのではなく、同じ処理を違うデータに対して行っている形になっている。


GPU内の各演算ユニットにはそれぞれ専用に高速のメモリ(レジスタ)を持っているが容量は少ない。また複数の演算ユニットをまとめてブロックを構成しており、ブロックごとに共有メモリ(たぶんSRAM)を持っている。共有メモリはブロック内の演算ユニットで共有されている。GPU内には複数のブロックがあり、ブロック全体で共有するグローバルなメモリがある(ビデオカードに乗ってるDDRとか)。メモリのアクセスは、演算ユニットに近いものほど高速。レジスタ、共有メモリは高速で1サイクルでアクセス可能だが、DDRはそれとくらべると非常に遅く数百サイクルかかる。


CPUはグローバルメモリ(DDR)へのデータの書き込み、読み出しを行う。データを書き込んだ後、GPUが実行する関数をCPU側から呼び出し、その実行結果がグローバルメモリへ書き込まれるので、それをCPUが読み出す、という流れで動作を行う。


GPUでの演算はスレッドごとに並列実行されるが、メモリへのアクセスは並列に実行できるとは限らない。レジスタは各ユニットごとに持っているので、並列アクセスになるが、共有メモリ、グローバルメモリはそうではない。
共有メモリは複数のバンクに分かれており、各スレッドがアクセスするバンクが異なっていれば並列にアクセスできるが、バンクが重なっているとコンフリクトが起こり並列アクセスにならなくなる。
グローバルメモリへのアクセスは、特定のルールを満たしたときのみ並列アクセスになり、そうでない場合は並列ではなくなる。特定のルールというのは、ハードウェアの構成に依存しており、その構成にあったアクセス方法をとれば、効率的にデータの読み書きが可能だが、そうでない場合多くの時間がかかってしまう。


というわけで、GPGPUを効果的に使うには、そのハードウェアを強く意識してプログラムを組まなければならない。CPUでマルチスレッドのプログラムを組む場合は、データの同期とかに気をつける程度で済むが、GPUでの並列処理の場合はそれに加えて、メモリの効率的なアクセスにも気を使ったプログラミングを行う必要がある。そのためには、メモリアクセスのハードウェア的な仕組みを理解してないと、うまくメモリを使えないということになる。そうなると、演算は高速になってもメモリアクセスがボトルネックになって結局高速化できない、ということもありうる。


以上のように、GPGPUを使いこなすための敷居は結構高いと思われる。パフォーマンスをあげるにはハードウェアを意識したプログラミングが必要で、まだまだ抽象化されているとはいえないように思う。まあ、それでもC言語ベースで開発できるようになっているので、だいぶ楽にはなってきてるのだろう。
逆に、うまく使いこなすことができれば一昔前のスーパーコンピュータ並みの性能を発揮させることができるわけで、これはこれで非常に楽しそうだ。パフォーマンスをあげるためのアルゴリズムの工夫なんかもやりがいがありそう。


ちなみに上で述べた内容はnVIDIAのCUDAを例にしている。が、たぶんAMDATI Streamとか、PS3のCELLとかでも基本的な考え方は同じだろうと思う。


GPGPUに関する解説は主にASCII.technologies (アスキードットテクノロジーズ) 2009年 12月号を参考にした。この本にはもっと詳しい解説がわかりやすく書かれている。CUDAやATI Streamでの具体的なプログラミング方法も載っているので、興味のある方にはおすすめ。GPGPU関連の本って、探してもなかなかみつからないんだよねー。