因为我的指针都指向不重叠的内存,所以我全力以赴,替换了传递给内核(及其内联函数)的指针,使其成为受限的,并尽可能使它们成为 const。然而,这增加了某些内核的寄存器使用量并减少了其他内核的使用量。这对我来说没有多大意义。
有谁知道为什么会这样?
因为我的指针都指向不重叠的内存,所以我全力以赴,替换了传递给内核(及其内联函数)的指针,使其成为受限的,并尽可能使它们成为 const。然而,这增加了某些内核的寄存器使用量并减少了其他内核的使用量。这对我来说没有多大意义。
有谁知道为什么会这样?
是的,它可以增加寄存器的使用。
参考编程指南__restrict__
:
此处的效果是减少了内存访问次数和减少了计算次数。这通过由于“缓存”负载和常见子表达式而增加的寄存器压力来平衡。
由于寄存器压力在许多 CUDA 代码中是一个关键问题,因此由于占用率降低,使用受限指针会对 CUDA 代码产生负面性能影响。
const __restrict__
至少有两个原因可能是有益的:
在支持它的架构上,它可以使编译器发现常量缓存的用途,这可能是一种性能增强功能。
如上面链接的编程指南部分所示,它可以使编译器进行其他优化(例如减少指令和内存访问),如果相应的寄存器压力没有成为问题,这也可以提高性能。
减少导致寄存器压力增加的指令和内存访问可能是不直观的。让我们考虑上面编程指南链接中给出的示例:
void foo(const float* a, const float* b, float* c) {
c[0] = a[0] * b[0];
c[1] = a[0] * b[0];
c[2] = a[0] * b[0] * a[1];
c[3] = a[0] * a[1];
c[4] = a[0] * b[0];
c[5] = b[0]; ... }
如果我们在上面的例子中允许指针别名,那么编译器就不能做很多优化,编译器本质上被简化为完全按照编写的代码执行。第一行代码:
c[0] = a[0] * b[0];
将需要 3 个寄存器。下一行代码:
c[1] = a[0] * b[0];
还需要 3 个寄存器,因为一切都是按原样生成的,所以它们可以是相同的 3 个寄存器,可以重复使用。示例的其余部分可能会发生类似的寄存器重用,从而导致总体寄存器使用/压力较低。
但是,如果我们允许编译器重新排序,那么我们必须预先为每个加载的值分配寄存器,并保留到该值退出。这种重新排序会增加寄存器的使用/压力,但最终可能会导致更快的代码(或者如果寄存器压力成为性能限制器,它可能会导致代码变慢。)