CUDA 堆栈帧大小增加 __forceinline__

Posted

技术标签:

【中文标题】CUDA 堆栈帧大小增加 __forceinline__【英文标题】:CUDA stack frame size increase by __forceinline__ 【发布时间】:2013-11-22 12:13:40 【问题描述】:

当我使用__forceinline__ 声明设备函数时,链接器会输出以下信息:

2>  nvlink : info : Function properties for '_ZN3GPU4Flux4calcILj512EEEvv':
2>  nvlink : info : used 28 registers, 456 stack, 15776 bytes smem, 320 bytes cmem[0], 0 bytes lmem

没有它的输出是:

2>  nvlink : info : Function properties for '_ZN3GPU4Flux4calcILj512EEEvv':
2>  nvlink : info : used 23 registers, 216 stack, 15776 bytes smem, 320 bytes cmem[0], 0 bytes lmem

为什么不使用__forceinline__ 时堆栈帧的大小会变小? 保持堆栈帧尽可能小有多重要? 感谢您的帮助。

【问题讨论】:

无法回答第一个问题,因为您没有提供有关__global____device__ 函数的任何信息。可以给出第二个问题的答案,并在下面报告。请访问CUDA Tag Info,详细了解如何获得有用的答案。引用 CUDA 标记信息:在您的问题中包含尽可能简单的代码示例,您更有可能得到有用的答案。如果代码简短且独立(因此用户可以自己测试),那就更好了。 【参考方案1】:

减少堆栈帧的主要原因是堆栈分配在本地内存中,而本地内存驻留在片外设备内存中。这使得访问堆栈(如果没有缓存)变慢。

为了说明这一点,让我举一个简单的例子。考虑案例:

__device__ __noinline__ void func(float* d_a, float* test, int tid) 
    d_a[tid]=test[tid]*d_a[tid];


__global__ void kernel_function(float* d_a) 
    float test[16];
    test[threadIdx.x] = threadIdx.x;
    func(d_a,test,threadIdx.x);

注意__device__ 函数声明为__noinline__。在这种情况下

ptxas : info : Function properties for _Z15kernel_functionPf
    64 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas : info : Used 7 registers, 36 bytes cmem[0]

即,我们有64 字节的堆栈帧。对应的反汇编代码为

MOV R1, c[0x1][0x100];
ISUB R1, R1, 0x40;
S2R R6, SR_TID.X;                    R6 = ThreadIdx.x
MOV R4, c[0x0][0x20];
IADD R5, R1, c[0x0][0x4];
I2F.F32.U32 R2, R6;                  R2 = R6 (integer to float conversion)              
ISCADD R0, R6, R1, 0x2;
STL [R0], R2;                        stores R2 to test[ThreadIdx.x]                                
CAL 0x50; 
EXIT ;                               __device__ function part
ISCADD R2, R6, R5, 0x2;
ISCADD R3, R6, R4, 0x2;
LD R2, [R2];                         loads d_a[tid]
LD R0, [R3];                         loads test[tid]
FMUL R0, R2, R0;                     d_a[tid] = d_a[tid]*test[tid]
ST [R3], R0;                         store the new value of d_a[tid] to global memory
RET ;

如您所见,test 从全局内存中存储和加载,形成堆栈帧(即16 floats = 64 bytes)。

现在将设备功能更改为

__device__ __forceinline__ void func(float* d_a, float* test, int tid) 
    d_a[tid]=test[tid]*d_a[tid];

即,将__device__ 函数从__noinline__ 更改为__forceinline__。在这种情况下,我们有

ptxas : info : Compiling entry function '_Z15kernel_functionPf' for 'sm_20'
ptxas : info : Function properties for _Z15kernel_functionPf
    0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads

也就是说,我们现在有一个空的堆栈帧。确实,反汇编的代码变成了:

MOV R1, c[0x1][0x100];               
S2R R2, SR_TID.X;                    R2 = ThreadIdx.x
ISCADD R3, R2, c[0x0][0x20], 0x2;    
I2F.F32.U32 R2, R2;                  R2 = R2 (integer to float conversion)
LD R0, [R3];                         R2 = d_a[ThreadIdx.x] (load from global memory)
FMUL R0, R2, R0;                     d_a[ThreadIdx.x] = d_a[ThreadIdx.x] * ThreadIdx.x
ST [R3], R0;                         stores the new value of d_a[ThreadIdx.x] to global memory
EXIT ;

如您所见,强制内联使编译器能够执行适当的优化,因此现在 test 已从代码中完全丢弃。

在上面的例子中,__forceinline__ 的作用与你所经历的相反,这也表明,如果没有任何进一步的信息,第一个问题是无法回答的。

【讨论】:

非常感谢您的解释。我没有发布代码的原因是它又长又复杂。对我来说似乎有点奇怪的是,当我删除 force_inline 修饰符时,编译器减少了堆栈帧。后来我意识到我在编译时启用了调试模式。在发布模式下是可以的。 如果是这样,为什么不将强制内联定义为默认值,强制内联有什么缺点

以上是关于CUDA 堆栈帧大小增加 __forceinline__的主要内容,如果未能解决你的问题,请参考以下文章

CUDA ptxas警告(进入的堆栈大小)

函数内联 inline,__inline,__forceinline

CUDA ptxas 警告(入口的堆栈大小)

关于 Cuda 的参考参数

CUDA 中大小为 4 的非法写入

static __forceinline或__forceinline static [关闭]