インタフェースの記事としてはまとめて書いたのだがブログとしてはほとんど書いてないので、今後調べたことをまとめてみる。
まずは書いた記事のおさらい
CPU/GPU/FPGA の得意な処理
CPU/GPU/FPGA でそれぞれがどのような処理を得意としているか?ということをまとめた。結局、お客さんにどうしたら速くなりますかね?と聞かれることが多いからね。
このテーマは繰り返し書いている。もう少しまとめたいなぁ。
第2部 CUDAプログラミング入門
CUDA が提供するプログラミング・モデルの理解と、どう書けば GPU のプログラムは速くなるのか?ということを丁寧に説明した(つもり)。
Warp(ワープ)を理解しないといけないと思う。
普通に書けばプログラムは確かに並列化されるが速くはならない(ことが多い)。
並列化プログラミング(GPU)で何が重要?
メモリ階層を考えて効率的にメモリ転送をするということが重要。効率的というのは、目的の計算が必要とする入力データの量がすくなくとも計算時間内に到達し消費されること。
もちろん、計算対象が並列化できるという条件はつく。並列化して速くなることが期待できるのは行列演算だ。
今度は逆から考えていこう
記事はいずれもプログラミングするという観点から考えている。なので、今度は逆から考えていこう。つまり、並列計算するハードウェアの都合から考えていこう。
GPU のハードウェアの基本はSIMD
第1章
行列演算やニューラル・ネットワーク
並列にすると速くなる処理
でも触れているが(残念なことにウェブでは言及しているところは見えない。必要な人はバックナンバーを買ってね。pdf でも売っているはず)、SIMD 的な考えで計算すると行列計算は速くなる(だろう)。
その SIMD とはなにか?というと Wikipedia から図と文章を引用しよう。「フリンの分類のひとつで、1つの命令を同時に複数のデータに適用する並列化の形態を指す」とある。
Wikipedia 図では PU(Processing Unit) となっていたので、ここでは説明の都合上 PE(Processing Element) と書き換えた。複数のPEに、同じインストラクションが配られる。そして、各 PE は異なるデータを Data pool から取ってきて計算する。同じインストラクションなのだが、入力データが違う。この図では表されていないが、結果は多くの場合 Data pool に PE ごとに違う領域へ書き戻されるだろう。
そんなことしないで、全部、違うインストラクションを実行可能にして並列にしてしまえばいいではないか?と思うかもしれない。メニーコアがそれに当たるが、しかし、そうするとハードウェアが複雑になってしまうのだ。AMD の EPYC 9754 は 128コアで 2.25GHz で動作する。値段も698,000 円(2024/3/9)ととんでもない価格になっている。
CPU のコア数と GPU の Shading Units を比較するのはフェアーではないが、NVIDIA の 3060 の場合 3584 の仮想的な(?)並列性が期待できる。「じゃ、逆に CPU やめて GPU にしちゃえばいい」という気もしてくるが(実際 GPU はだんだん CPU に機能的に近づいている気がする)、GPU はやはり汎用性のあるプログラムは専門じゃなく、並列計算に特化したデバイスなのである。
汎用的な処理は CPU で柔軟に、同時並行可能な行列計算のような単純な計算が大量にある場合は GPU でと、場合分けをすることで、世の中うまく行くようになっている。今のところは。
積和演算を考える
例えば単純な積和演算を考えていこう。並列性を考えると難しくなるので、シンプルに1つの PE を考える。さっきの絵から他の PE を減らしてみよう。
積和演算なので、C風に書けば a += b * c を計算することになる。+= というのが曲者だ。= すると次のような式になり、a を読み込むことが明示的になる。
入出力を考えると図はもうちょい詳細化できる。さらに Data Pool って何?と考えてみると、なんとなくレジスタなのかなという気がしてくる。つまり1クロックですぐにアクセスできるメモリ。図を書き直してみる。
入力データは 32bit の浮動小数点数としよう。b と c を足してから、a に加えて、a へと書き戻す。この作業は複数クロックが掛かりそうだ。
これを高速にいっぱい同時に実行するのが GPU だ!すごいね!と言ってもいいのだが、せっかく(私が) FPGA マガジンの筆者でもあるのでもうちょい、クロック・レベルで掘り下げていこう。
To Be Continue...