簡単に言えば、単一のワープ トランザクションのサイズには 128 B の制限があるということです (バス幅が原因だと思います)。したがって、ワープに 256 B の合体データが必要な場合は、2 番目の 128 B の命令を再生する必要があります。
一般に、トランザクションは 32B、64B、および 128B セグメントのデータのみを移動します。ワープ トランザクションがこれらのいずれにも適合しない場合は、命令を少なくとも 1 回再生します。合体パターンはこれを避けることはできませんが、トランザクションを最小限に抑えるのに役立ちます。たとえば、ワープ内の Bytes の合体アクセスは、32B トランザクションを取得します。ワープ内で結合された 4B アクセス (int または float) は、単一の 128B トランザクションを取得します。
次のカーネルを検討してください。
__global__ void
gmemtest(const double* const src, double* const dest, const int size,
const int eleMoved){
int block_fst = blockIdx.x*blockDim.x*eleMoved;
size_t thread_fst = block_fst + threadIdx.x*eleMoved;
#pragma unroll
for(size_t i = 0; i < eleMoved; i++){
if( thread_fst + i < size )
dest[thread_fst + i] = src[thread_fst + i];
}
elemoved
次に、サイズ 1、2、4、および 8 で実行します。カーネルelemoved
が大きくなるにつれて、リプレイが増加することがわかります。次のホスト側ループは、128 と 256 のブロック サイズでそれらすべてにヒットします。
for(size_t j = 1; j<3; j++){
for(size_t i = 1; i<=8; i *= 2){
size_t n_threads = j*128;
size_t ele_per_thread = i;
size_t tot_threads = ((SIZE-1)/ele_per_thread)+1;
size_t n_blocks = ((tot_threads - 1)/n_threads)+1;
gmemtest<<<n_blocks,n_threads>>>(d_src,d_dest,SIZE,ele_per_thread);
}
}
実行nvprof --print-gpu-trace --metrics inst_replay_overhead
すると次のことがわかります。
==22053== Profiling result:
Device Context Stream Kernel Instruction Replay Overhead
Tesla K20c (0) 1 2 gmemtest(double cons 0.191697
Tesla K20c (0) 1 2 gmemtest(double cons 0.866548
Tesla K20c (0) 1 2 gmemtest(double cons 3.472359
Tesla K20c (0) 1 2 gmemtest(double cons 7.444514
Tesla K20c (0) 1 2 gmemtest(double cons 0.175090
Tesla K20c (0) 1 2 gmemtest(double cons 0.912531
Tesla K20c (0) 1 2 gmemtest(double cons 4.067719
Tesla K20c (0) 1 2 gmemtest(double cons 7.576686
実際には、ワープに値するデータのようなものを移動している場合、これに遭遇する可能性がありdouble2
ます。
本当にパフォーマンス関連の問題に取り組みたい場合は、この講演を十分にお勧めできません: Micikevicius - "Performance Optimization: Programming Guidelines and GPU Architecture Details Behind They"