GPUのメモリ階層
レジスタファイルは高速でアクセスができ、バンド幅も非常に広いが、容量は制限される。このため、GDDR5 DRAMで作られる大容量のデバイスメモリが接続されるが、デバイスメモリはアクセス速度が遅いので、この差を埋めるため、メモリ階層が設けられる。
図3-18に示すように、NVIDIAのGPUではレジスタファイルの次にシェアードメモリとレベル1データキャッシュ、リードオンリーデータキャッシュが並列に置かれ、レベル1データキャッシュとリードオンリーデータキャッシュは、レベル2キャッシュでバックアップされる。さらにその先にはレベル2キャッシュ、GDDR5 DRAMで構成されるデバイスメモリがあるという階層になっている。
レジスタファイルの次の階層の主力となるシェアードメモリ
GPUでは数1000のスレッドが並列に実行され、それらが独立にアドレス計算を行なってメモリにアクセスする。つまり、すべてのスレッドが同じ命令列を実行している状態で、ロードやストア命令を実行すると、1つの命令が数1000のロード/ストアになってメモリに押し寄せる。これを順番に1つずつ捌いていたのでは、メモリアクセスに膨大な時間が掛かってしまう。
このため、初期のGPUのメモリ階層は図3-19のようになっている。基本的には演算器は、レジスタファイルとシェアードメモリ(AMDはLocal Data Share:LDSと呼ぶ)を使って処理を行い、速度が遅いデバイスメモリはあまり使わないという計算モデルである。
このようにすれば、シェアードメモリは同じグループ(NVIDIAはWarpと呼び、AMDはWavefrontと呼ぶ)の32スレッド(Waverfrontは64スレッド)の並列アクセスをサービスできれば良い。デバイスメモリは大きな容量が必要であるので、別チップのDRAMが使われるが、シェアードメモリは、例えば16KBと容量が小さいので、高速SRAMを使ってチップ上に作ることができる。
シェアードメモリは、図3-20のようになっており、例えば、32スレッドのグループの場合、32バンクというような多数のバンクを持つ。32スレッドからのアクセスは、そのアドレス[7:2]に従って、どのバンクにアクセスするかが決まり、クロスバを使って目的のアドレスを担当するバンクに接続する。
32スレッドからのアクセスが全て異なるバンクを使う場合は、シェアードメモリは32個のアクセスを同時に処理することができる。しかし、各バンクは1つの32bitデータの読み書きしかできないので、複数のスレッドからのアクセスが同一バンクに重なってしまうと、1回では処理できず、読み出しや書き込みを繰り返して順番に処理して行くことになる。ただし、同一バンクの同一のアドレスに複数の読み出しアクセスが重なった場合は、同じデータをそれらのスレッドに返せば良いので、この場合は、1回の読み出しで済む。なお、書き込みが同じアドレスに重なった場合は、結果は不定、あるいは、どれか一つの書き込みデータとなるが、どれが書かれるかは決まっていないなどという実装が多い。
各バンクは32bit幅の独立のメモリとなっており、独立してアドレスすることができる。従って、図3-20で、バンク0は1段目のアドレス0、バンク1は2段目のアドレス132、バンク3は3段目の264のように、バンクごとに異なる段のデータをアクセスすることができるようになっている。
グラフィックスの描画処理で言えば、ホストCPUからの3D図形のデータはDMAなどでデバイスメモリに転送され、描画処理が起動されると、各32スレッドグループはデバイスメモリからデータのブロックをシェアードメモリに読み込んで処理を行う。そして、生成された表示画像をデバイスメモリに書き込んで行く。このように処理を行えば、大部分の描画処理の演算はデバイスメモリを使わず、レジスタファイルとシェアードメモリを使って実行できる。
また、デバイスメモリとシェアードメモリの間の転送は、32スレッド分を纏めて行えば、スレッドごとにデバイスメモリをアクセスする必要もなくなる。
3Dグラフィックスのように大量のポリゴンデータがストリームとして入力される処理では、それを適当に区切ってデバイスメモリからシェアードメモリにコピーし、描画処理が終わったら、結果をシェアードメモリからデバイスメモリにコピーするという処理を繰り返せば良いので、シェアードメモリとデバイスメモリの間のデータ転送の頻度は少なく、記述は比較的容易である。