为什么在OpenCL中禁止递归?


19

我想使用OpenCL加速渲染光线跟踪图像,但是我注意到Wikipedia页面声称Open CL中禁止递归。这是真的?由于我在光线跟踪时大量使用递归,因此需要大量的重新设计才能从速度上受益。阻止递归的基本限制是什么?有什么办法解决吗?


2
GPU以不同的方式工作。(某些体系结构)没有全局“程序栈”的概念,因此在这些函数中无法进行递归函数调用。OpenCL可能采用最低的公分母,因此完全不允许它在GPU之间保持可移植性。较新的CUDA硬件似乎在某些时候引入了对递归的支持:stackoverflow.com/q/3644809/1198654
glampert 2015年

Answers:


27

实质上是因为并非所有的GPU都可以支持函数调用,并且即使支持,函数调用也可能非常慢或存在栈深度非常小的限制。

着色器代码和GPU计算代码似乎在各处都有函数调用,但是在正常情况下,编译器会将它们全部内联100%。GPU执行的机器代码包含分支和循环,但没有函数调用。但是,由于明显的原因,无法内联递归函数调用。(除非某些参数是编译时常量,否则编译器可以折叠它们并内联整个调用树。)

为了实现真正的函数调用,您需要一个堆栈。在大多数情况下,着色器代码根本不使用堆栈-GPU具有大型寄存器文件,并且着色器可以将所有数据始终保留在寄存器中。使堆栈难以工作是因为(a)您需要大量的堆栈空间才能同时提供所有可能发生的所有扭曲,并且(b)GPU内存系统经过优化可大量批处理内存事务以实现高吞吐量,但这是以等待时间为代价的,因此,我的猜测是,诸如保存/恢复本地变量的堆栈操作会非常慢。

从历史上看,硬件级别的函数调用在GPU上并不是太有用,因为在编译器中内联所有内容更为有意义。因此GPU架构师并没有专注于使其快速发展。如果将来对有效的硬件级调用有需求,可能会做出一些不同的权衡,但是(与工程中的所有内容一样)它将在其他地方产生成本。

就光线追踪而言,人们通常处理这种事情的方式是通过创建正在追踪过程中的光线队列。无需递归,而是将射线添加到队列中,并在高层的某个位置具有循环,该循环不断进行处理,直到所有队列都为空。但是,如果您从经典的递归raytracer开始,则确实需要对渲染代码进行大量重组。有关更多信息,请阅读Wavefront Path Tracing,这是一篇不错的文章。


6
我不太愿意分享这个秘密,但是我很幸运,它具有固定的最大跳出计数,并具有固定大小的堆栈(以及具有固定迭代次数的循环)来处理此问题。另外(这是真正的秘密调味酱imo!)我的材料是反射性的或折射性的,但绝不能是两者兼有的,这使得光线反弹时不会裂开。所有这些的最终结果是递归类型的光线跟踪渲染,但是通过固定大小的迭代而不是递归。
艾伦·沃尔夫

像尾递归?
Tanmay Patil

由于尾递归函数可以转换为迭代函数,因此不需要堆栈即可执行尾递归。OpenCL编译器是否不会自动执行此操作?
安德森·格林
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.