两个事实:CUDA 5.0允许您在不同的对象文件中编译CUDA代码,以便稍后进行链接。 CUDA架构2.x不再自动内联函数。
像在C / C ++中一样,我在__device__ int foo()
中实现了一个函数functions.cu
,并将其标题放在functions.hu
中。函数foo
在其他CUDA源文件中调用。
当我检查functions.ptx
时,我看到foo()
溢出到本地内存。出于测试目的,我对foo()
的所有内容进行了评论,并将其return 1;
根据.ptx
仍然溢出到本地内存中。 (我无法想象它是什么,因为函数什么都不做!)
但是,当我将foo()
的实现移动到头文件functions.hu
并添加__forceinline__
限定符时,则没有任何内容写入本地内存!
这里发生了什么?为什么CUDA不会自动内联这么简单的功能?
单独标题&的整点。实现文件是为了让我的生活更轻松地维护代码。但是如果我必须在标题和__forceinline__
中添加一堆函数(或者所有函数),那么它会破坏CUDA 5.0的不同编译单元的目的......
有什么方法吗?
简单,真实的例子:
functions.cu:
__device__ int foo
(const uchar param0,
const uchar *const param1,
const unsigned short int param2,
const unsigned short int param3,
const uchar param4)
{
return 1; //real code commented out.
}
上述功能溢出到本地内存。
functions.ptx:
.visible .func (.param .b32 func_retval0) _Z45fooPKhth(
.param .b32 _Z45foohPKhth_param_0,
.param .b64 _Z45foohPKhth_param_1,
.param .b32 _Z45foohPKhth_param_2,
.param .b32 _Z45foohPKhth_param_3
)
{
.local .align 8 .b8 __local_depot72[24];
.reg .b64 %SP;
.reg .b64 %SPL;
.reg .s16 %rc<3>;
.reg .s16 %rs<4>;
.reg .s32 %r<2>;
.reg .s64 %rd<2>;
答案 0 :(得分:4)
并非所有本地内存使用都代表溢出。被调用的函数需要遵循ABI调用约定,包括创建位于本地内存中的堆栈帧。当nvcc传递命令行开关-Xptxas -v时,编译器会报告堆栈使用情况并将其作为子组件溢出。
目前(CUDA 5.0),CUDA工具链不支持跨编译单元边界的函数内联,就像一些主机编译器那样。因此,在单独编译的灵活性(例如重新编译大型项目的一小部分,编译时间较长,创建设备端库的可能性)和通常由函数产生的性能增益之间存在权衡内联(例如,消除由于ABI调用约定导致的开销,实现额外的优化,例如跨函数边界的不断增长)。
单个编译单元中的函数内联由编译器启发式控制,该编译器启发式试图确定内联是否可能在性能方面有利可图(如果可能的话)。这意味着并非所有函数都可以内联。程序员可以使用函数属性__forcinline__
和__noinline__
覆盖启发式。