GPUのデバイスメモリ

GPUは非常に高性能の演算器群を持っているので、それらに供給するデータの読み込みや処理結果の書き込みには大量のメモリアクセスが必要になる。例えば、DDR3-1866のDRAMインタフェースを4チャネルもつXeon CPUのピークメモリバンド幅は約60GB/sであるが、高速のデータR/WができるGDDR5メモリを使うNVIDIAのK40 GPUでは288GB/s、AMDのFire Pro W9100 GPUでは320GB/sのメモリバンド幅を持っている。

CPUで使われるメモリはDDR3、そして最近ではDDR4の採用が始まっているが、1本の信号線のデータ転送速度は2Gbit/sを若干超える程度である。一方、GPUが使うGDDR5(第5世代Graphic DDR)メモリは7Gbpsの転送速度を誇り、DDR4の3倍くらいの速度である。また、CPUでは64bit幅のDDR3/4のDIMMを4チャネル程度の接続であるが、ハイエンドGPUでは32bit幅のGDDR5チップを12~16チャネル接続することで、高いメモリバンド幅を得ている。

話がちょっと逸れるが、GPUをCPUと同一チップに集積するAMDのAPUと呼ぶ製品群やIntelのCoreプロセサでは、DDR3メモリをCPUとGPUで共用しているが、やはり、GPUのメモリバンド幅不足は否めない。より高性能のグラフィクスを追及するゲームコンソール向けのソニーのPlayStation 4はCPU、GPUの共通メモリとしてGDDR5メモリを使っているが、MicrosoftのXbox 360では共通メモリとしては、バンド幅は低いが消費電力が小さくメモリ容量の大きいDDR3メモリを使い、GPUには10MBのeDRAMを付けてDDR3メモリでは不足するメモリバンド幅を補っている。

最近は、複数のDRAMチップをTSV(Through Silicon Via)技術を使って積層し、例えば1024信号という多数の信号ピンを持たせ、GPUとの間はシリコンインタポーザを使って高密度配線を行うことにより、より高いメモリバンド幅を持つGPUが出始めているが、これについては項を改めて説明することにしたい。

デバイスメモリのアドレスの付け方

K40 GPUは、チップの容量が1GBのGDDR5 DRAMを12個搭載している。最初のチップがアドレス0~1GB-1、2番目のチップがアドレス1GB~2GB-1、3番目のチップがアドレス2GB~3GB-1のようにするのが一番簡単である。しかし、これでは、全部のスレッドが0~1GB-1のアドレスをアクセスするようなプログラムを実行した場合、1個のメモリチップだけが使われ、メモリバンド幅は全体の1/12の24GB/sしか得られない。実際は、K40 GPUは1つのメモリコントローラで2個のGDDR5 DRAMを担当する構造になっているので、この2個に交互にアドレスを振ってやれば、各ペアは2GBで、48GB/sのメモリバンド幅を得ることができる。しかし、これでもピークの1/6の性能しか得られない。

GDDR5 DRAMは32bit幅で8回の連続アクセスを行うので、アクセスの単位は256bit、つまり32バイト単位で読み書きされる。そして、2個のGDDR5チップのペアでは64バイト単位の読み書きとなる。

最初のペアの64バイトを0番地から、2番目のペアの64バイトを64番地から、3番目のペアの64バイトを128番地からという風にアドレスを割り当てると、連続したアドレスをアクセスするプログラムでも6ペア全てのメモリチップが使われてピークの288GB/sのバンド幅が得られる。

図3-51 アクセスを分散させるGDDR5メモリのアドレスの付け方

しかし、AMDのFire Pro W9100 GPUのように8ペアのGDDR5 DRAMを使う場合は、ペア数が2のベキであるので、0~7番のペアの次は0番のペアに戻るというのは簡単に実現できるが、K40 GPUのように6ペアの場合は、どのペアのどの番地をアクセスするかを求めるには384で割り算を行って商(行番号)と剰余(どのGDDR5ペアか)を求める必要が出てくる。

このためには、÷6を計算する割り算器をメモリコントローラに内蔵するか、あるいはアドレス変換を行うページテーブルに割り算を行った結果に相当するアドレスを記入しておき、テーブルの参照で割り算を行う必要がある。前者は、割り算器というハードウェアが必要になり、物量や消費電力が多少増加し、アクセス時間が割り算の計算の分だけ遅くなる。後者は、本来のメモリ管理とこのアドレス変換を1つのテーブルで行おうとすると、管理が複雑になるという問題がある。