5

通常建议降低每个线程的寄存器压力以增加warp 占用率,从而通过warp 级多线程(TLP)提供更多隐藏延迟的机会。为了减少寄存器压力,可以使用更多的每个线程本地内存或每个线程块共享内存。CUDA nvcc 编译器也可以强制每个线程使用更少的寄存器。这种方法对于具有良好算术延迟的工作负载很有用,即 ALU 操作与内存读写访问请求的比率很高。然而,对于计算量很少且内存访问更频繁的延迟关键型应用程序,这种方法实际上往往会降低性能。

在此类延迟关键应用程序的情况下,将尽可能多的数据带入片上寄存器或共享内存中更有意义,然后在用全局内存中的下一个数据块替换之前尽可能多地使用它。当然,通过增加寄存器压力,warp 占用率会降低,但现在我们正在使用快速片上寄存器隐藏片外内存延迟。增加每个线程寄存器使用的方法是通过展开循环或计算每个线程的更多输出数据来增加 ILP(这也基本上通过对更多输入执行相同的工作来增加 ILP)。这种方法基本上是由 Volkov (Better Performance at Lower Occupancy) 提出的。

现在 nvcc 编译器驱动程序有一个名为 maxrregcount 的命令行选项,它允许更改每个线程的寄存器使用情况。使用此选项一次可以强制编译器减少每个线程寄存器的使用,但不能强制它增加它。我有一个案例,我想增加每个线程寄存器的使用,但我不能在内核中展开循环,因为循环边界是数据相关的和动态的。到目前为止,我已经尝试了一些技巧,但是我已经没有关于如何增加每个线程寄存器使用率的想法。谁能建议增加单个 CUDA 线程的寄存器使用率的方法?

4

3 回答 3

2

在某种程度上,这个问题重复了 Forcing CUDA to use register for a variable。您已经很好地总结了这些选项。如果您不能通过展开和显式使用标量变量来强制使用寄存器,那么我认为您可能会被卡住。

请注意,即使具有动态边界的循环也可以部分手动展开。您只需检查循环展开部分内的边界。这可能有助于增加寄存器的使用。

我还认为增加寄存器使用和减少延迟之间没有保证的直接关系,所以你真的应该专注于减少延迟,而不是特别关注寄存器使用。

如果您想减少整体内核延迟,那么您应该尝试一些事情。

  • 启动的线程块不能超过可以在 GPU 上同时运行的线程块(由占用计算器确定)。
  • 最小化内核函数参数的数量,因为这些需要在内核启动期间进行初始化(并且具有许多参数会因此增加启动开销)。
于 2012-08-31T04:42:46.063 回答
2

有趣的问题!我也在尝试这种使用 ILP 的方法来提供更好的性能!事实上,由于我受制于较旧的 GPU 架构,每个线程分配的寄存器较少,因此使用 ILP 实际上可以提高性能,因为它通过循环展开(独立指令)释放寄存器以进行更多计算工作!

我想知道你有多少个嵌套循环?如果内循环无法展开,可能会上升一个级别并寻找机会?

为了增加每个线程的寄存器使用量,您是否减少了启动的块数(使用较少的线程)?
要增加寄存器/线程的使用,请加载多于 1 组数据以并行执行。

它在循环的每次迭代中都是独立的吗?我相信关键是寻找独立的计算。批量执行如何。假设循环计数为 N,将其拆分为 N/M 并独立地对它们进行 omcpute?

当你给出一点线索时,很难给出建议:P

于 2012-08-31T08:15:01.693 回答
-2

这个问题的构架方式就像是在问,“我怎样才能在商店花更多的钱买牛奶?” 问题是颠倒的。你应该问的是,“我有一定数量的钱。我如何用它来获得尽可能多的牛奶?”

好吧,不是最好的类比,但基本上,问题的表述好像增加寄存器数量本身就是目标,而目标当然是提高性能。

所以,首先要确定的是,您是否拥有您认为的那么多的寄存器?如果寄存器是内核中的占用限制因素。当内核受内存限制时,更改代码以便使用更多寄存器可能不是一个好主意。

如果您确定占用受到其他因素的限制,那么您可以询问是否可以通过使用更多寄存器来提高性能(然后寄存器“空闲”,直到寄存器成为占用限制因素)。

为此,您然后开始寻找时空权衡的选项。

于 2012-08-31T05:32:25.213 回答