在CUDA中增加每个线程的寄存器使用量

5
通常建议降低每个线程的寄存器压力以增加Warp占用率,从而通过Warp级多线程(TLP)提供更大的机会来隐藏延迟。为了降低寄存器压力,可以使用更多的每个线程本地内存或每个线程块共享内存。CUDA nvcc编译器也可以强制每个线程使用更少的寄存器。这种方法对于具有良好算术延迟的工作负载特别有用,即ALU操作与内存读写访问请求的比率高。但是对于延迟关键应用程序,在其中计算非常少,而且频繁进行内存访问时,实际上会降低性能。
在这种延迟关键的应用程序情况下,将尽可能多的数据带入芯片上的寄存器或共享内存中,然后在用全局内存中的下一块数据替换之前尽可能多地使用它。当然,通过增加寄存器压力,Warp占用率会降低,但现在我们正在使用快速的芯片上寄存器来隐藏芯片外内存延迟。增加每个线程的寄存器使用量的方法是通过展开循环来增加ILP或计算更多输出数据以使线程(this also increases ILP basically by doing same work on more inputs)。这种方法最初由Volkov(Better Performance at Lower Occupancy)提出。
现在nvcc编译器驱动程序有一个命令行选项称为maxrregcount,它允许更改每个线程的寄存器使用情况。使用此选项可以强制编译器降低每个线程的寄存器使用量,但无法强制其增加。我有一个案例,我想增加每个线程的寄存器使用量,但我无法展开内核内的循环,因为循环边界是数据相关和动态的。到目前为止,我尝试了一些技巧,但我已经没有思路如何增加每个线程的寄存器使用量。是否有人能建议一些方法来增加单个CUDA线程的寄存器使用量?

很抱歉,但是这个问题对我来说完全荒谬。 - Roger Dahl
@Roger Dahl:如果你刚刚阅读了我提到的那篇论文,你就会理解我在这里想要表达的观点。 - nurabha
3个回答

2
在某种程度上,这个问题与强制CUDA使用寄存器变量重复了。你已经很好地总结了选项。如果你不能通过展开和显式标量变量使用来强制使用寄存器,则可能会陷入困境。
请注意,即使是具有动态边界的循环也可以部分手动展开。你只需要在循环的展开部分中检查边界。这可能有助于增加寄存器使用率。
我还认为,增加寄存器使用率与减少延迟之间没有明确的直接关系,因此你应该专注于减少延迟,而不是特别关注寄存器使用。
如果你想减少整个内核的延迟,那么有一些事情你应该尝试。
  • 启动的线程块数量不要超过GPU可以并行运行的数量(由占用计算器确定)。
  • 最小化内核函数参数的数量,因为这些需要在内核启动期间初始化(因此具有许多参数可能会增加启动开销)。

我已经阅读了你之前的帖子。实际上,我现在正在尝试手动展开。起初似乎不可能,但昨晚我发现了一些技巧。我实际上试图强制编译器分配更少的寄存器,以获得更高的占用率,但增加占用率似乎会降低我的内核性能。这就是为什么我认为增加每个线程的寄存器应该可以提高性能。当然,会有一个阈值,在此之后,占用率的降低可能会影响性能。但我认为我需要探索这种权衡。 - nurabha
我正在使用Tesla C1060,每个线程的寄存器使用量为32,这限制了SM的占用率为50%或两个线程块。在我看来,当我使用更多的线程块(每个SM 4个线程块而不是2个)启动我的内核时,我会得到更好的结果。目前,我的内核参数数量约为15个,我想这可能太高了。也许我需要连接几个数组并减少参数数量。 - nurabha

2
有趣的问题!我也正在尝试使用ILP来提高性能!实际上,由于我受限于旧版GPU的架构,每个线程分配的寄存器较少,使用ILP实际上可以通过循环展开(独立指令)为更多计算工作释放寄存器,从而提高性能!
我想知道你有多少个嵌套循环?如果内部循环无法展开,则可能向上一级寻找机会?
为了增加每个线程的寄存器使用率,您是否减少了启动块的数量(使用较少的线程)? 为了增加每个线程的寄存器使用率,请加载多个数据集以并行执行。
在每次循环迭代中是否独立?我认为关键是寻找独立计算。如何分批处理?假设循环计数为N,将其拆分为N / M并独立计算?
当你给出很少的提示时,很难给出建议 :P

1
我也在使用旧的Tesla Architecture 1.3计算能力。我的顺序算法有四层循环嵌套。实现CUDA内核后,通过并行化外部两个循环,将它们减少到了两个。我无法展开内部循环,现在我正在集中精力手动展开外部循环。基本上,我采取的方法是在每个外部循环迭代中产生更多的输出,因为每个循环迭代都是独立的。我考虑过平铺式方法,但在我的情况下不起作用。 - nurabha
有关改进的任何更新吗?=) 就我而言,我尝试将其移植到Kepler架构GPU上,但加速效果并不显著。现在,Kepler为每个线程分配了更多的寄存器。 - Hong Zhou
我继续使用Tesla C1060进行实验,但是没有获得任何加速效果。我尝试了2倍和3倍展开循环,寄存器的使用量从最初的31增加到41,然后再增加到52,但是性能并没有太大改善。不过无论如何,我的内核已经平均提升了32倍,最大提升了56倍的速度。 - nurabha

-2
这个问题的提出方式就像是在问:“我怎样才能在商店里多花钱买牛奶?”这个问题本身就是颠倒的。你应该问的是:“我有一定的金额,如何用它来获得尽可能多的牛奶?”
好吧,这个比喻不是最好的,但基本上,这个问题的陈述方式好像增加寄存器计数本身就是目标,而实际上,目标当然是提高性能。
因此,首先要确定的是,你是否拥有你认为拥有的那么多寄存器?如果寄存器是限制内核占用率的因素,那么当内核受到内存限制时,改变代码以使用更多的寄存器可能不是一个好主意。
如果你已经确定了占用率受其他因素限制,那么你可以问问是否可以通过使用更多的寄存器来提高性能(直到寄存器成为占用率限制因素为止,寄存器就是“空闲”的)。
为此,你可以开始查看时空权衡的选项。

2
我认为你对占用率的评论是错误的。 Nurav不需要减少他的占用限制资源,因为增加占用可能会产生他想要的完全相反的效果。它可能会增加延迟。他想要降低延迟,而不是增加吞吐量。增加占用不能降低延迟。在一定程度上(当有空闲插槽可填充时),增加占用不会增加延迟,但一旦SM每个周期完成一个指令,进一步增加将只会增加延迟。 - harrism
@harrism:没错。增加我的内核占用实际上会降低性能。增加占用率只能在一定程度上降低延迟。 - nurabha
nurava和@harrism:感谢您提供的信息和反馈。我已经阅读了这篇论文,现在明白了你们在谈论什么。它与我原来认为的有所不同,但是非常合理。我可能会在未来的内存绑定内核中尝试应用这些技术。 - Roger Dahl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接