:) 当我试图管理我的内核资源时,我决定研究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个寄存器。那么为什么编译时显示只用了2个寄存器?
- 看到汇编后我意识到threadId、blockId的数据类型是。这是CUDA规范中定义的吗?还是可能因CUDA驱动版本不同而有所变化?
- 谁能解释一下这行代码:
mul.wide.u16 %r1, %rh1, %rh2;
?%r1
是u32
,为什么要使用wide
而不是u32
? - 寄存器的名称是如何选择的?在我的情况下,我理解了
%r
,但我不理解h
、(null)、d
等部分。它是基于数据类型长度选择的吗?例如:16位使用h
,32位使用(null),64位使用d
? - 如果我将内核的最后两行替换为
out[idx] = in[idx];
,那么当我编译程序时,它说使用了3个寄存器!现在怎么可能使用更多的寄存器呢?
请忽略我的测试内核没有检查数组索引是否越界的事实。
非常感谢。