GPUのレジスタ
GPUの仕様を見ると、32論理PEあたり、数1000個の32bitレジスタを持っていると書かれている。CPUの汎用レジスタの数は16個とか32個であるので、それに比べて圧倒的に多いレジスタ数である。
しかし、マルチスレッドCPUの場合は、整数レジスタや浮動小数点レジスタなどの汎用レジスタは、スレッドごとに用意されており、いくつスレッドがあろうとも、各スレッドで使えるレジスタ数は変わらないという構造になっているが、GPUの場合は、巨大なレジスタを同時並列的に切り替えて実行するすべてのスレッドで分割して使用するという構造になっている。
例えば、8192レジスタの場合、これは32論理PEの合計のレジスタ数であるので、1論理PEあたりで使えるレジスタは256レジスタである。そして、各論理PEが16ワープをサイクル(あるいは2サイクル)ごとに切り替えながら実行する場合は、1つのスレッドが使えるレジスタ数は平均16個になってしまう。
NVIDIAのGK110というハイエンドのGPUのSMX(マルチコアCPUの1つのコアに相当し、GK110チップには15個のSMXが搭載されている)という単位では、65,536レジスタがあるが、SMXは最大2048スレッドを並列に実行するので、この場合、平均的に各スレッドが使用できるレジスタ数は32ということになる。
このようにスレッドあたりで見ると、GPUのレジスタ数はCPUとあまり変わらない。CPUの場合は、整数演算用のレジスタと浮動小数点演算用のレジスタが独立に存在するのが一般的であるが、GPUの場合は、整数も浮動小数点数も同じレジスタを使う。このため、各スレッドが使えるレジスタ数としては、GPUの方がレジスタ数が少なく制約がきついとも言える。
ただし、GPUの場合は各スレッドが同じ数のレジスタを使うとは限らず、NVIDIAのGPUでは、コンパイル時に何個のレジスタを使うかを指定できるようになっている。ただし、少数のレジスタしか使わないようにすると、レジスタに入りきらない値はメモリに格納し、必要になったらメモリから読み出して使うというケースが増え、性能は低下する。一方、切り替えるスレッド数が少ない場合は、スレッドあたりで使用できるレジスタ数は増加し、メモリへの退避、復元の回数を減らせる。しかし、切り替えるスレッド数を減らしすぎると実行時間の長い命令では命令のレーテンシが見えてきて待ちが生じてしまうというトレードオフが存在する。
このようにGPUでは、スレッドによって使うレジスタ数が異なるため、レジスタのアクセス機構は図3.4のようになっていると考えられる。Curr Threadがそのサイクルに実行するスレッド番号を指しており、これを使って、スレッドテーブルからスレッドのStatus、PC(Program Counter)とレジスタ番号のベース値を読み出す。また、論理的にはこのテーブルは命令キャッシュ2ライン分の命令バッファを含んでおり、実行する命令のPC付近の命令をキャッシュから読み出して保持している。また、この図では書かれていないが、命令バッファに格納している命令が無くなってくると、命令キャッシュから自動的にフェッチして命令バッファに入れる機構が設けられている。これはCPUの場合の命令バッファのフィル機構と同じである。
そしてPCを使って、命令バッファから実行する命令を命令セレクタで取り出し、レジスタ番号のフィールドであるRA、RB、RCにReg Baseの値を加えてレジスタファイルをアクセスする。このようにすればスレッドごとにレジスタを使用する範囲を任意に指定することができる。なお、この図では書き込み側については省略しているが、結果を書き込むレジスタ番号についても同様に計算し、パイプラインレジスタを使って、書き込みサイクルまで遅延させて使えば良い。
GPUのレジスタファイルアクセスは、図3.4に示したように、スレッドテーブルの読み出し、Reg Baseの加算などの処理が必要であり、また、レジスタのエントリ数が多いので、CPUのレジスタのように高速でアクセスできるように作るのは難しい。このため、例えば、Curr ThreadレジスタからReg Baseの加算までを1サイクル、Reg File内部のデコーダの出力までに1サイクル、アレイを読み出すのに1サイクルというように、レジスタファイルのアクセスがパイプライン構造になっているのではないかと思われる。
シングルスレッドを高速に実行することを目指すCPUの場合は、レジスタファイルのアクセスサイクル数が増えることは性能を大きく低下させてしまうが、GPUの場合は、同一スレッドの次の命令が出てくるのは、10命令サイクル(20サイクル)とか後になるので、レジスタアクセスに3サイクルかかっても性能的な悪影響はないと思われる。