WebGPU 中的 storageBarrier 实际上是做什么的?

use*_*993 8 shader gpgpu webgpu wgsl

因此,我正在探索 WebGPU,并认为在其中实现基本神经网络将是一个有趣的练习。对 GPU 着色器编程和神经网络了解甚少,而且我对 WebGPU(w3.org/TR/webgpu 和 w3.org/TR/WGSL)的唯一参考是高度技术性的,这确实使它变得非常有趣。

不管怎样,不知何故,我已经把自己的方式搞乱了,以至于我实际上可以在小型网络上正确地执行前馈和反向传播,而且与我的 js cpu 实现相比,它的速度也非常快,尽管我确信我严重未充分利用硬件。

我已经到了想要尝试更大网络的地步,但在工作组和同步执行方面我有点不知所措。为了保持简单,我将把问题集中在前馈操作上:

目前,我正在调度与神经网络中最宽层相对应的线程数量。这个想法是,每个线程计算当前层中单个神经元的值,然后遇到障碍,然后每个线程一起移动到下一层,如此下去。

问题是,我只有两种设置屏障的方法 - 要么 workgroupBarrier() 要么结束执行并为下一层分派一堆新线程。

第一个的问题是它只能在工作组内工作,而且我只能在性能开始受到影响之前将工作组设置得这么大,因为据我了解,由于需要共享内存,只有单个 CU 可以在工作组上工作。如果我将工作组设置为 256x256,那么它将被切成多个块,单个 CU 必须在其余硬件闲置时进行处理。这限制了我的网络宽度,限制了单个 CU 可以容纳的线程数量,非常蹩脚。

第二个的问题非常明显 - 单独的调度速度很慢,比我测试的障碍慢得多。

现在,我根本不使用工作组共享内存,我想做的就是分派任意数量的线程并设置全局屏障。据我了解,WebGPU 没有全局屏障......除了 storageBarrier ?

即使在阅读了 w3.org 上关于它是什么的 2 句话之后,我仍然不知道它是什么,但我认为它与内存访问同步有关,而不是与全局屏障有关。我确实测试了它,结果是正确的,但是即使我从代码中消除了所有障碍,结果也是正确的,我猜这是 GPU 的 SIMT 执行风格的好处。但是,我不需要它“可能正确”,我需要保证正确,所以我需要一个全局屏障。是 storageBarrier 吗?如果不是的话那是什么?

额外问题 - 为什么工作组和派遣有 3 个维度,为什么不只有一个?

小智 9

很好的问题。

首先简单一点:

额外问题 - 为什么工作组和派遣有 3 个维度,为什么不只有一个?

这就是 GPU 的内部结构。计算着色器是在直接图形渲染之后发展起来的。2D 调度与 2D 图像处理(例如卷积)很好地对应,并且图形渲染也具有 3D 纹理。

屏障可帮助您协调对读写内存的访问。问题是:您正在协调哪些代理(调用),以及您正在控制对哪些内存的访问。

障碍在两个维度上进行协调:

  • 不同的调用。
  • 不同的地址空间。

调用按层次分组:

  • 工作组:并行运行并共享“工作组”地址空间中变量的访问的调用。
  • 调度中的所有调用,即同一调度启动的所有工作组。同一调度中的不同工作组可能会同时运行,也可能会串行运行。因此,该模型不支持同一调度中工作组之间明确定义的协调。

地址空间:

  • “工作组”地址空间:保存在单个工作组内共享的变量
  • “存储”:保存在调度中的所有调用(即所有工作组)之间共享的变量(缓冲区) 。这些可以是只读的或读写的。
  • “uniform”:类似于存储,但始终是只读的,因此协调很简单。

鉴于此,我们现在可以说:

  • storageBarrier 协调单个工作组中对“存储”地址空间中缓冲区的调用访问。
  • workgroupBarrier 协调单个工作组中对“工作组”地址空间中变量的调用的访问。

具体来说,合理的思考方式是地址空间 X(X 是“工作组”或“存储”)的屏障是执行中的一个点,其中:

  • 工作组中的所有调用都等待彼此到达屏障
  • 对地址空间“X”中变量的所有正在进行的写入已完成
  • 然后所有调用都会解除阻塞,并且可以在屏障之后继续执行。
  • 在屏障之后,从地址空间“X”中的变量进行的任何读取都将“看到”在屏障之前启动的写入。

(这不是规范中的描述方式,因为它受到过度约束。但那是针对语言律师的。)

您会注意到:您只能协调同一工作组中的调用。这意味着不支持通过非原子操作执行此操作的方法:

  • 将数据写入一个工作组中的“存储”缓冲区
  • 在不同的工作组中但在同一个调度中读回相同的数据

为什么?金属着色语言障碍不支持它。对不起。详情请参见https://github.com/gpuweb/gpuweb/pull/2297

(如果您希望继续讨论内存模型定义和测试,该模式称为“消息传递”模式。)

注意:“CU”或“计算单元”在 GPU 语言规范中并不是一个明确定义的术语。这就是特定 GPU 的组织和营销方式,但这只是一个细节。

好的,关于如何构建你的工作组。如果您的数据结构与您的工作组相同,这一切都会很容易。但否则你必须阻止你的数据,即。对问题进行分区以适应,或者使单个调用一次处理一个数据块。这是最大化利用率和并行性的关键。有很多关于如何做到这一点的文献/教程,特别是对于矩阵乘法之类的事情。