我正在开发一个内核函数,它具有几个矢量运算,如标量和矢量积。内核使用大量寄存器,因此占用率非常低。我正在尝试减少使用的寄存器数量,以提高占用率。
例如,考虑以下__device__
函数在两个float3
之间执行标量积:
__device__ float dot(float3 in1, float3 in2) { return in1.x * in2.x + in1.y * in2.y + in1.z * in2.z; }
如果我使用
生成.ptx
文件
nvcc -ptx -gencode arch=compute_52,code=sm_52 -rdc=true simpleDot2.cu
(文件simpleDot2.cu
仅包含__device__
函数的定义),我基本上获得了
// .globl _Z3dot6float3S_
.visible .func (.param .b32 func_retval0) _Z3dot6float3S_(
.param .align 4 .b8 _Z3dot6float3S__param_0[12],
.param .align 4 .b8 _Z3dot6float3S__param_1[12]
)
{
.reg .f32 %f<10>;
ld.param.f32 %f1, [_Z3dot6float3S__param_0+8];
ld.param.f32 %f2, [_Z3dot6float3S__param_0];
ld.param.f32 %f3, [_Z3dot6float3S__param_0+4];
ld.param.f32 %f4, [_Z3dot6float3S__param_1+8];
ld.param.f32 %f5, [_Z3dot6float3S__param_1];
ld.param.f32 %f6, [_Z3dot6float3S__param_1+4];
mul.f32 %f7, %f3, %f6;
fma.rn.f32 %f8, %f2, %f5, %f7;
fma.rn.f32 %f9, %f1, %f4, %f8;
st.param.f32 [func_retval0+0], %f9;
ret;
}
从.ptx
代码中,似乎使用了许多9
个寄存器,可能会降低这些寄存器。我知道.ptx
代码不是GPU执行的最终代码。
问题
是否有机会重新安排.ptx
代码中的寄存器使用情况,例如回收寄存器f1
- f6
,以减少占用寄存器的总数?
非常感谢您的帮助。
答案 0 :(得分:2)
TL; DR 要先订购,否。
PTX
既是虚拟ISA又是编译器中间表示。 PTX代码中使用的寄存器是虚拟寄存器,并且与GPU的物理寄存器没有固定的关系。 CUDA工具链生成的PTX代码遵循SSA(静态单一赋值)约定。这意味着每个虚拟寄存器只写入一次。换句话说:当指令产生结果时,它被分配给新的寄存器。这意味着更长的内核可能会使用数千个寄存器。
在CUDA工具链中,ptxas
组件将PTX代码编译到机器代码(SASS)。因此,尽管有名称,但这不是汇编程序,而是优化编译器,它可以执行循环展开,CSE(公共子表达式消除)等等。最重要的是,ptxas
负责寄存器分配和指令调度,以及特定于GPU架构的所有优化。
因此,对寄存器使用问题的任何检查都需要关注机器代码,可以使用cuobjdump --dump-sass
提取。此外,程序员对所使用的寄存器数量的影响非常有限,因为ptxas
在确定寄存器分配时使用了许多启发式算法,特别是在寄存器使用与性能之间进行权衡:早期调度负载往往会增加寄存器压力。寿命范围,在CSE期间创建临时变量或创建感应变量以减少循环中的强度。
目标计算能力为3.0或更高的CUDA现代版通常在确定这些权衡时做出了很好的选择,程序员很少需要考虑注册压力。目前尚不清楚提问者在这方面的问题是什么。
CUDA中用于控制最大寄存器使用的文档化机制是-maxrregcount
nvcc
命令行标志,它适用于整个编译单元,以及允许控制的__launch_bounds__
属性在每个内核的基础上。有关详细信息,请参阅CUDA文档。除此之外,通过选择pxtas
优化级别-Xptxas -O{1|2|3}
(默认为-O3
)或重新安排源代码,可以尝试来影响注册用量,或使用倾向于简化生成代码的编译器标志,例如-use_fast_math
。
当然,这种间接方法可能具有许多通常不可预测的其他效果,并且所实现的任何期望的结果将是“脆弱的”,例如,通过更改为新版本的工具链轻松销毁。