:) 当我试图管理我的内核资源时,我决定研究 PTX,但有几件事我不明白。这是我写的一个非常简单的内核:
__global__
void foo(float* out, float* in, uint32_t n)
{
uint32_t idx = blockIdx.x * blockDim.x + threadIdx.x;
uint32_t one = 5;
out[idx] = in[idx]+one;
}
然后我使用编译它:nvcc --ptxas-options=-v -keep main.cu
我在控制台上得到了这个输出:
ptxas info : 0 bytes gmem
ptxas info : Compiling entry function '_Z3fooPfS_j' for 'sm_10'
ptxas info : Used 2 registers, 36 bytes smem
结果 ptx 如下:
.entry _Z3fooPfS_j (
.param .u64 __cudaparm__Z3fooPfS_j_out,
.param .u64 __cudaparm__Z3fooPfS_j_in,
.param .u32 __cudaparm__Z3fooPfS_j_n)
{
.reg .u16 %rh<4>;
.reg .u32 %r<5>;
.reg .u64 %rd<8>;
.reg .f32 %f<5>;
.loc 15 17 0
$LDWbegin__Z3fooPfS_j:
.loc 15 21 0
mov.u16 %rh1, %ctaid.x;
mov.u16 %rh2, %ntid.x;
mul.wide.u16 %r1, %rh1, %rh2;
cvt.u32.u16 %r2, %tid.x;
add.u32 %r3, %r2, %r1;
cvt.u64.u32 %rd1, %r3;
mul.wide.u32 %rd2, %r3, 4;
ld.param.u64 %rd3, [__cudaparm__Z3fooPfS_j_in];
add.u64 %rd4, %rd3, %rd2;
ld.global.f32 %f1, [%rd4+0];
mov.f32 %f2, 0f40a00000; // 5
add.f32 %f3, %f1, %f2;
ld.param.u64 %rd5, [__cudaparm__Z3fooPfS_j_out];
add.u64 %rd6, %rd5, %rd2;
st.global.f32 [%rd6+0], %f3;
.loc 15 22 0
exit;
$LDWend__Z3fooPfS_j:
} // _Z3fooPfS_j
现在有一些我不明白的事情:
- 根据 ptx 程序集,使用了 4+5+8+5=22 个寄存器。那为什么它
used 2 registers
在编译期间说? - 查看程序集,我意识到 threadId、blockId 等的数据类型是
u16
. 这是在 CUDA 规范中定义的吗?或者这可能在不同版本的 CUDA 驱动程序之间有所不同? - 有人可以向我解释这一行:
mul.wide.u16 %r1, %rh1, %rh2;
?%r1
是u32
,为什么使用wide
而不是u32
? - 寄存器的名称是如何选择的?在我的花瓶中,我理解
%r
部分,但我不理解h
,(null),d
部分。它是根据数据类型长度选择的吗?即:h
对于 16 位,对于 32 位为空,d
对于 64 位? - 如果我用 this 替换内核的最后两行
out[idx] = in[idx];
,那么当我编译程序时,它会说使用了 3 个寄存器!现在怎么可能使用更多的寄存器?
请忽略我的测试内核不检查数组索引是否超出范围这一事实。
非常感谢。