这个存储库是用并行函数式编程技术编写的射线跟踪器的雏形集合。其目的是调查一个相当小而简单的问题,在某种程度上,函数式编程实现了经常承诺的轻松并行性,以及所产生的代码在客观意义上是否真的很快。基准技术大多是粗糙的,因此假设只有较大的相对差异是有意义的。我欢迎贡献,因为我不太相信我的代码是最优的。我至多精通这里展出的一种语言。我也欢迎其他语言的新实现!
另请注意,这不是一个好的光线跟踪器。它不会产生特别漂亮的图像。之所以选择它,原因很简单,因为它表达了两种有趣的平行关系(见下文),而且即使是丑陋的图像也比仅仅是一个数字更有趣。使用了两个场景。第一个是rgbbox:
第二个场景很有趣,因为负载是不平衡的:allobjects位于像素的下半部分。
实际渲染场景,由BVH加速。这在很大程度上是简单的数据并行,但每个像素的工作量可能很大。
以下测量是针对1000x1000渲染的。我使用的是Ryzen 1700X(8核、16线程)CPU和Vega64GPU。同一列中的比较枚举数。
Haskell实现使用严格的语言杂注来禁用核心模块中的惰性。这对运行时的影响大约是1.5-2倍。massiv库用于并行阵列,是大部分性能的来源。
在几次错误启动之后,F#在使用.NET内核时运行得相当快。主要技巧似乎是使用内联函数和显式值类型。
MPL(它是标准ML的MLton的面向并行的分支)绝对是这里的明星。代码可读,以完全自然的风格编写,性能卓越。
虽然允许实现使用单精度浮点(如果它们愿意的话),但是使用双精度时,Scala实现实际上要快得多。
虽然Futhark速度很快,但代码要长得多,也更复杂。这特别是因为BVH的建设。在所有其他实现中,BVH被表示为一个简单的递归分而治之的函数,这也很容易通过fork-join技术并行化。由于Futhark不支持递归,所以它使用了TeroKarras在“最大化BVH、八叉树和k-dTree的构造中的并行性”一文中提出的自底向上技术,这实际上是一个相当快的技术(虽然不适用于这里使用的小场景),但它比递归公式长约200行。CPU计时使用早期的试验性多核后端,并发出咔哒声来编译C代码。
铁锈是最快的CPU语言。这并不令人惊讶,因为ASIT有一个成熟的编译器,它的默认行为就是解开所有东西,这正是您在这个程序中所需要的。
从上表中看不到的是,大多数实现在其原始公式中都明显慢了很多。只有Futhark、MPL和Rust与它们的第一个直接实现相比基本上没有改变。对于其他人,大部分性能归结为各种低级别的调整,特别是避免了装箱和分配。这并不完全出乎意料,但我仍然感到遗憾的是,当谈到函数式语言的性能时,我们必须更多地考虑编译器,而不是语言。
乔恩·哈罗普(Jon Harrop)的光线追踪器语言比较(Ray Tracer Language Comparison)是本页的灵感来源。主要区别在于我关注的是并行性。这里的光线追踪器还需要从场景数据中构造一个显式的BVH,而乔恩·哈罗普的光线追踪器使用函数公式来描述他的场景的递归结构。