レジスタの使用は、
maxrregcount
コンパイラオプションまたは起動境界で説明されている起動境界を使用して制御できます。
私の理解から(そして私が間違っている場合は訂正してください)、-maxrregcount
は.cu
ファイル全体が使用できるレジスタの数を制限しますが、__launch_bounds__
修飾子は各__global__
カーネルのmaxThreadsPerBlock
とminBlocksPerMultiprocessor
を定義します。これら2つは同じタスクを実行しますが、2つの異なる方法で実行します。
私の使用法では、パフォーマンスを最大化するために、スレッドごとに40
レジスタが必要です。したがって、-maxrregcount 40
を使用できます。 __launch_bounds__(256, 6)
を使用して40
レジスタを強制することもできますが、これによりレジスタのロードとストアが流出します。
これらのレジスタの流出を引き起こす2つの違いは何ですか?
この質問の序文は、CUDA C Programming Guide
を引用して、
カーネルが使用するレジスタが少ないほど、マルチプロセッサ上に存在するスレッドとスレッドブロックが増える可能性があり、パフォーマンスを向上させることができます。
現在、__launch_bounds__
とmaxregcount
は、2つの異なるメカニズムによってレジスタの使用を制限しています。
__launch_bounds__
nvcc
は、カーネル起動セットアップのパフォーマンスと一般性のバランスをとることにより、__global__
関数で使用されるレジスターの数を決定します。別の言い方をすれば、使用されるレジスタの数をこのように選択すると、ブロックあたりのスレッド数とマルチプロセッサあたりのブロック数が異なる場合に「有効性が保証」されます。ただし、コンパイル時にブロックあたりの最大スレッド数と(場合によっては)マルチプロセッサあたりの最小ブロック数のおおよそのアイデアが利用できる場合は、この情報を使用して、そのような起動用にカーネルを最適化できます。言い換えると
#define MAX_THREADS_PER_BLOCK 256
#define MIN_BLOCKS_PER_MP 2
__global__ void
__launch_bounds__(MAX_THREADS_PER_BLOCK, MIN_BLOCKS_PER_MP)
fooKernel(int *inArr, int *outArr)
{
// ... Computation of kernel
}
nvcc
がそのような起動構成のレジスタ数を「最適な」方法で選択できるように、起動構成の可能性をコンパイラに通知します。
MAX_THREADS_PER_BLOCK
パラメーターは必須ですが、MIN_BLOCKS_PER_MP
パラメーターはオプションです。また、カーネルがMAX_THREADS_PER_BLOCK
より大きいブロックあたりのスレッド数で起動された場合、カーネルの起動は失敗することに注意してください。
制限メカニズムは、Programming Guide
で次のように説明されています。
起動境界が指定されている場合、コンパイラはまず、カーネルが使用するレジスタ数の上限
L
を導き出し、minBlocksPerMultiprocessor
スレッドのminBlocksPerMultiprocessor
ブロック(またはmaxThreadsPerBlock
が指定されていない場合は単一のブロック)がマルチプロセッサ上に存在できるようにします。次に、コンパイラは次の方法でレジスタの使用を最適化します。
- 初期レジスタ使用量が
L
よりも高い場合、コンパイラはそれがL
以下になるまでそれをさらに減らしますが、通常はローカルメモリ使用量の増加や命令数の増加を犠牲にします。
したがって、__launch_bounds__
はレジスタの流出につながる可能性があります。
maxrregcount
maxrregcount
は、コンパイラにレジスタの使用を再配置させることにより、使用されるレジスタの数を__launch_bounds__
とは異なり、ユーザーが設定した数に単純にハード制限するコンパイラフラグです。コンパイラが課せられた制限を下回らない場合、コンパイラは単にローカルメモリ(実際にはDRAM
)にそれをこぼします。このローカル変数でさえ、グローバルDRAM
メモリ変数に格納され、L1、L2にキャッシュできます。