- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在用 C++ 编写路径跟踪器,我想尝试将资源最密集的代码实现到 CUDA 或 OpenCL 中(我不确定该选择哪个)。
我听说我的显卡的 CUDA 版本不支持递归,这是我的路径追踪器大量使用的东西。
因为我用 Python 和 C++ 编写了代码,所以我将发布一些简化的 Python 代码以提高可读性:
def Trace(ray):
hit = what_object_is_hit(ray)
if not hit:
return Color(0, 0, 0)
newRay = hit.bouceChildRayOffSurface(ray)
return hit.diffuse * (Trace(newRay) + hit.emittance)
我尝试手动展开函数,是一个确定的模式(d
是diffuse
和e
是发射率
):
Level 1: d1 * e1
Level 2: d1 * d2 * e2
+ e1
Level 3: d1 * d2 * d3 * e3
+ d1 * d2 * e2
+ e1
Level 4: d1 * d2 * d3 * d4 * e4
+ d1 * d2 * d3 * e3
+ d1 * d2 * e2
+ e1
虽然我可能是错的......
我的问题是,如何在 while
循环中实现这段代码?
我在考虑使用这种格式:
total = Color(0, 0, 0)
n = 1
while n < 10: # Maximum recursion depth
result = magical_function()
if not result: break
total += result
n += 1
我以前从未真正处理过解开递归函数的任务,因此非常感谢任何帮助。谢谢!
最佳答案
在递归函数中,每次递归调用发生时,调用者的状态被保存到堆栈中,然后在递归调用完成时恢复。要将递归函数转换为迭代函数,您需要将挂起函数的状态转换为显式数据结构。当然,您可以在软件中创建自己的堆栈,但通常可以使用一些技巧来提高代码效率。
此答案适用于此示例的转换步骤。您可以将相同的方法应用于其他循环。
让我们再看看你的代码:
def Trace(ray):
# Here was code to look for intersections
if not hit:
return Color(0, 0, 0)
return hit.diffuse * (Trace(ray) + hit.emittance)
一般来说,递归调用必须回到调用函数,这样调用者才能完成它正在做的事情。在这种情况下,调用者通过执行加法和乘法“完成”。这会产生像这样的计算d1 * (d2 * (d3 * (... + e3) + e2) + e1))
。我们可以利用加法分配律和乘加结合律将计算转化为[d1 * e1] + [(d1 * d2) * e2] + [(d1 * d2) * d3 ) * e3] + ...
。请注意,本系列中的第一项仅指迭代 1,第二项仅指迭代 1 和 2,依此类推。这告诉我们,我们可以即时计算这个系列。此外,这个系列包含系列 (d1, d1*d2, d1*d2*d3, ...)
,我们也可以即时计算它。将其放回代码中:
def Trace(diffuse, emittance, ray):
# Here was code to look for intersections
if not hit: return emittance # The complete value has been computed
new_diffuse = diffuse * hit.diffuse # (...) * dN
new_emittance = emittance + new_diffuse * hit.emittance # (...) + [(d1 * ... * dN) + eN]
return Trace(new_diffuse, new_emittance, ray)
在新的循环中,被调用者完成后,调用者没有工作可做;它只是返回被调用者的结果。调用方没有要完成的工作,因此它不必保存任何状态!我们可以覆盖旧参数并返回到函数的开头而不是调用(不是有效的 Python,但它说明了这一点):
def Trace(diffuse, emittance, ray):
beginning:
# Here was code to look for intersections
if not hit: return emittance # The complete value has been computed
new_diffuse = diffuse * hit.diffuse # (...) * dN
new_emittance = emittance + new_diffuse * hit.emittance # (...) + [(d1 * ... * dN) + eN]
(diffuse, emittance) = (new_diffuse, new_emittance)
goto beginning
最后,我们将递归函数转化为一个等价的循环。剩下的就是用 Python 语法表达它。
def Trace(diffuse, emittance, ray):
while True:
# Here was code to look for intersections
if not hit: break
diffuse = diffuse * hit.diffuse # (...) * dN
emittance = emittance + diffuse * hit.emittance # (...) + [(d1 * ... * dN) + eN]
return emittance
关于python - "Unrolling"递归函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6300695/
我阅读了有关循环展开的文档。它解释说,如果将展开因子设置为 1,则程序将像使用 #pragma nounrolling 一样工作。 但是,该文件不包括#pragma unroll(0) 案例..由于
我正在用 C++ 编写路径跟踪器,我想尝试将资源最密集的代码实现到 CUDA 或 OpenCL 中(我不确定该选择哪个)。 我听说我的显卡的 CUDA 版本不支持递归,这是我的路径追踪器大量使用的东西
在我的一个JAVA项目中,我通常必须将巨大的数组与标量相乘。因此我想通过使用所谓的循环展开来编写一种方法。到目前为止我已经想出了这个: public static float[] arrayTimes
在最近的代码审查中,出现了关于 @Unroll 注释属于类级别还是方法级别的问题。该类的大多数方法(但不是全部)都需要 @Unroll。如果在类级别声明并且并非类的所有方法都需要它,声明 @Unrol
我有同样的问题: Expression templates: improving performance in evaluating expressions? 我的目标是展开这个表达式的循环 auto
在NVIDIA的制作精良reduction optimization documentation ,他们最终得到一个看起来像这样的 warpReduce: Template __device__ v
我是 CUDA 新手,我无法理解循环展开。我编写了一段代码来理解该技术 __global__ void kernel(float *b, int size) { int tid = block
URL 缩短器在 Twitter 等空间受限的媒体中很有用。但是这样的危险已经得到充分讨论(有限的生命周期、隐藏恶意链接、可用性等)。但是有没有一种很好的方法来预先解析来自 goo.gl 或 bit.
我试图告诉我的编译器使用#pragma unroll 为我展开一个循环。但是,迭代次数由编译时变量决定,因此循环需要展开那么多次。像这样: #define ITEMS 4 #pragma unroll
在 C++11 中是否有一种简单的方法可以做到这一点?如果可能的话,我想同时保留多重继承和循环访问包中所有静态函数的能力。 #include struct A { static void foo()
给定一个 Python 元组 t = v1, v2, v3 是否有一个实用程序可以解压这些元组以便给定: def foo(v1,v2,v3): pass 取而代之的是: foo(t[0],t[1],t
假设我有一个看起来像这样的循环: for(int i = 0; i : xor ebx,ebx 0x08048406 : push ecx 0x08048407 : xor
摘自 GCC 手册: -funroll-loops Unroll loops whose number of iterations can be determined at co
不确定如何调用它,但假设您有一个看起来像这样的类: class Person { public string Name; public IEnumerable Friends; } 然
我使用 Lisp 宏的第一步...... (defconstant width 7) (defconstant height 6) ... ; board is a 2D array of width
例如假设我有以下两个函数 function a(param1) { console.log(param1); } function b(func, params) { func(par
我一直认为 foo2 下面的函数比 foo3 快,直到经过测试。 所有代码如下: #include #include #include #include struct session {
我想知道 C++ 编译器是否会像他们目前对“正常”循环所做的那样展开基于范围的循环以最大化性能,或者在某些情况下基于范围的循环会比正常循环慢? 非常感谢。 最佳答案 基于范围的循环相当于: { a
在 CUDA 中,可以使用 #pragma 展开循环。 unroll 指令通过增加指令级并行度来提高性能。 #pragma可以选择后跟一个数字,指定循环必须展开多少次。 不幸的是,文档没有给出何时应该
我有一段代码,我希望 LLVM 按特定因子展开其中的所有循环。我正在使用以下命令: opt -mem2reg -simplifycfg -loops -lcssa -loop-simplify -lo
我是一名优秀的程序员,十分优秀!