カーネルの設計には問題があることに注意する必要があります。つまり、このコードを使用して行う測定は、倍精度の命令スループットとはまったく関係がありません。
すべての倍精度演算を含む計算ループの結果はメモリ書き込みで使用されないため、コンパイラの最適化によって削除されます。CUDA 5コンパイラは、カーネルに対して次のPTXを発行します。
.visible .entry _Z12bench_singlePf(
.param .u32 _Z12bench_singlePf_param_0
)
{
.local .align 8 .b8 __local_depot0[8];
.reg .b32 %SP;
.reg .b32 %SPL;
.reg .s32 %r<16>;
mov.u32 %SPL, __local_depot0;
cvta.local.u32 %SP, %SPL;
add.u32 %r3, %SP, 0;
.loc 2 13 1
cvta.to.local.u32 %r4, %r3;
// inline asm
mov.u32 %r1, %clock;
// inline asm
// inline asm
mov.u32 %r2, %clock;
// inline asm
st.local.v2.u32 [%r4], {%r2, %r1};
cvta.const.u32 %r5, $str;
// Callseq Start 0
{
.reg .b32 temp_param_reg;
.param .b32 param0;
st.param.b32 [param0+0], %r5;
.param .b32 param1;
st.param.b32 [param1+0], %r3;
.param .b32 retval0;
.loc 2 13 1
call.uni (retval0),
vprintf,
(
param0,
param1
);
ld.param.b32 %r6, [retval0+0];
}
// Callseq End 0
.loc 2 14 1
sub.s32 %r7, %r2, %r1;
cvta.const.u32 %r8, $str1;
st.local.u32 [%r4], %r7;
// Callseq Start 1
{
.reg .b32 temp_param_reg;
.param .b32 param0;
st.param.b32 [param0+0], %r8;
.param .b32 param1;
st.param.b32 [param1+0], %r3;
.param .b32 retval0;
.loc 2 14 1
call.uni (retval0),
vprintf,
(
param0,
param1
);
ld.param.b32 %r9, [retval0+0];
}
// Callseq End 1
.loc 2 15 2
ret;
}
2つのクロックロード命令は隣接しており、他のコードはへの呼び出しのみprintf
です。そのPTXには計算ループはありません。
コンパイラがループ結果が未使用であると推測して最適化できないように、カーネルを再設計する必要があります。1つのアプローチは次のようになります。
__global__
void bench_single(float *data, int flag=0)
{
int i;
double x = 1.;
clock_t start, end;
start = clock();
for(i=0; i<1000000; i++) {
x = x * 2.388415813 + 1.253314137;
}
end = clock();
printf("End and start %d - %d\n", end, start);
printf("Finished in %d cycles\n", end-start);
if (flag) {
data[blockIdx.x] = x;
}
}
カーネルの最後の条件付き書き込みは、コンパイラーがループを最適化するのを妨げるため、コンパイラーは次のPTXを発行します。
.visible .entry _Z12bench_singlePfi(
.param .u32 _Z12bench_singlePfi_param_0,
.param .u32 _Z12bench_singlePfi_param_1
)
{
.local .align 8 .b8 __local_depot0[8];
.reg .b32 %SP;
.reg .b32 %SPL;
.reg .pred %p<3>;
.reg .f32 %f<2>;
.reg .s32 %r<28>;
.reg .f64 %fd<44>;
mov.u32 %SPL, __local_depot0;
cvta.local.u32 %SP, %SPL;
ld.param.u32 %r6, [_Z12bench_singlePfi_param_0];
ld.param.u32 %r7, [_Z12bench_singlePfi_param_1];
add.u32 %r10, %SP, 0;
.loc 2 13 1
cvta.to.local.u32 %r1, %r10;
// inline asm
mov.u32 %r8, %clock;
// inline asm
mov.f64 %fd43, 0d3FF0000000000000;
mov.u32 %r27, 1000000;
BB0_1:
.loc 2 10 1
fma.rn.f64 %fd4, %fd43, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd5, %fd4, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd6, %fd5, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd7, %fd6, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd8, %fd7, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd9, %fd8, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd10, %fd9, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd11, %fd10, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd12, %fd11, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd13, %fd12, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd14, %fd13, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd15, %fd14, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd16, %fd15, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd17, %fd16, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd18, %fd17, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd19, %fd18, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd20, %fd19, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd21, %fd20, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd22, %fd21, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd23, %fd22, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd24, %fd23, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd25, %fd24, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd26, %fd25, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd27, %fd26, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd28, %fd27, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd29, %fd28, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd30, %fd29, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd31, %fd30, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd32, %fd31, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd33, %fd32, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd34, %fd33, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd35, %fd34, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd36, %fd35, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd37, %fd36, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd38, %fd37, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd39, %fd38, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd40, %fd39, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd41, %fd40, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd42, %fd41, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
fma.rn.f64 %fd43, %fd42, 0d40031B79BFF0AC8C, 0d3FF40D931FE078AF;
.loc 2 9 1
add.s32 %r27, %r27, -40;
setp.ne.s32 %p1, %r27, 0;
@%p1 bra BB0_1;
cvta.to.global.u32 %r5, %r6;
// inline asm
mov.u32 %r11, %clock;
// inline asm
.loc 2 13 1
st.local.v2.u32 [%r1], {%r11, %r8};
cvta.const.u32 %r12, $str;
// Callseq Start 0
{
.reg .b32 temp_param_reg;
.param .b32 param0;
st.param.b32 [param0+0], %r12;
.param .b32 param1;
st.param.b32 [param1+0], %r10;
.param .b32 retval0;
.loc 2 13 1
call.uni (retval0),
vprintf,
(
param0,
param1
);
ld.param.b32 %r14, [retval0+0];
}
// Callseq End 0
.loc 2 14 1
sub.s32 %r15, %r11, %r8;
cvta.const.u32 %r16, $str1;
st.local.u32 [%r1], %r15;
// Callseq Start 1
{
.reg .b32 temp_param_reg;
.param .b32 param0;
st.param.b32 [param0+0], %r16;
.param .b32 param1;
st.param.b32 [param1+0], %r10;
.param .b32 retval0;
.loc 2 14 1
call.uni (retval0),
vprintf,
(
param0,
param1
);
ld.param.b32 %r17, [retval0+0];
}
// Callseq End 1
.loc 2 16 1
setp.eq.s32 %p2, %r7, 0;
@%p2 bra BB0_4;
.loc 2 17 1
cvt.rn.f32.f64 %f1, %fd43;
mov.u32 %r18, %ctaid.x;
shl.b32 %r19, %r18, 2;
add.s32 %r20, %r5, %r19;
st.global.f32 [%r20], %f1;
BB0_4:
.loc 2 19 2
ret;
}
ここで、コンパイラがループを部分的に展開した場所から、浮動小数点の乗算追加命令の素晴らしいストリームがあることに注意してください。
グレッグ・スミスが指摘したように、命令スケジューリングのレイテンシーを克服するのに十分なワープが特定のSMで実行されるまで、命令スループットの実際の測定値を取得することを期待するべきではありません。これはおそらく、少なくとも1つの大きなブロックを実行する必要があることを意味します。また、printf呼び出しは、スループットに大きな悪影響を与えることに注意してください。ブロックごとにスレッドが1つしかない場合は、結果を書き出すか、(さらに良いことに)グローバルメモリに保存すると、より代表的な番号が得られます。多数のブロックを実行すると、平均化できる多数の測定値が得られます。最後のチェックとして、オブジェクトコードを次のように逆アセンブルする必要があります。cudaobjdump
アセンブラがクロック読み取り命令の位置を移動しないようにするため。そうしないと、依存しているカーネル内のタイミングが壊れます。古いバージョンのアセンブラには、カーネルCコードまたはPTXに挿入された一連のクロック読み取りの機能を壊す可能性のある命令の並べ替えの習慣がありました。