使用WGSL在GPU上实现细胞自动机。

4
我正在编写一个物理模拟程序,类似于细胞自动机。每个步骤都依赖于前一个步骤,但更确切地说,每个单元格需要其自身和其直接相邻的单元格的状态来计算其新状态。我使用两个缓冲区,在每个步骤中交替使用它们的角色(多次读取/单次写入)。
我正在使用WGSL(WebGPU),目前,对于每个步骤(整个网格更新,换句话说是t+1),我调用一个分派(以确保步骤之间的同步),但性能非常慢。(编辑:因为我没有正确使用工作组)
我尝试在着色器中直接使用循环执行步骤,但我无法在每个步骤之间同步所有工作组。因为我怀疑CPU和GPU之间的通信是限制因素。(剧透警告:不是这样的)
我尝试使用storageBarrierworkgroupBarrier,但它们不起作用(同步未发生)。尽管如此,如果我只在它们之间使用两个连续步骤和一个障碍,我可以将性能提高2倍,这意味着我在分派期间浪费了大部分时间。结果几乎是完美的(意味着一些同步没有发生,但并没有对结果产生太大影响)。

编辑:前面的段落是一个误解,我的测试结果是误导性的。

我读到说,使用当前 WGSL 规范无法在单个调度中同步所有工作组。但是我不明白为什么会有 workgroupBarrierstorageBarrier

如何强制所有工作组在元胞自动机的每个步骤之间进行同步?

但更一般地说,我想我不是第一个使用这种直接邻居依赖关系在 GPU 上编写元胞自动机的人:

如何快速编写使用 GPU 的元胞自动机?

1个回答

3

我不确定你编写程序的具体方式。 我猜测你正在计算,也许你正在尝试读取和写入同一个缓冲区?

通常使用两个缓冲区来编码细胞自动机。 一个用于上一步中的状态(只读),另一个用于当前步骤中的新状态(只写)。 每次调用可以从上一步中读取多个值,并通常在当前缓冲区上写入一个值。

在每个步骤结束时,您可以将它们交换。 这样就不需要任何障碍,并且可以在图形或计算管道中实现。


https://github.com/lemire/SIMDgameoflife/blob/master/include/basicautomata.h 是一个CPU SIMD(AVX2)实现的例子。它填充了一个邻居计数的临时缓冲区,然后原地更新状态,这对于单个线程来说几乎是相同的事情。并且可以轻松地适应只更新单独的目标,这对于CPU-SIMD实现可能更有效,也许避免了存储/重新加载和第二次数据遍历。 - Peter Cordes
使用一个临时缓冲区然后将其复制到原始缓冲区,只是额外进行了一次拷贝的双缓冲操作(这样你下次就不必交换缓冲区)。 - Axiverse
是的。但请注意,这不仅仅是在最后一次遍历中进行复制,每次遍历都会完成部分实际工作。(除了顶部和底部行之外,将其优化为一次遍历应该很容易,并且这就是我建议的方式。我本来希望找到一个实际使用两个缓冲区的高效方法的示例,但结果发现它多做了一次加载+存储的操作。) - Peter Cordes

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