【发布时间】:2019-09-05 23:32:15
【问题描述】:
我在写入缓冲区时遇到了主要瓶颈。
我想做的很简单。 首先,我使用了两个全局 id(我使用的是 image2d)。每个线程读取 9 个像素值,位置 (x,y) 处的像素及其 8 个相邻像素,基本上是一个 3x3 方形块。 这项工作由每个线程完成。现在,我计算了一些值,我想将每个线程的结果写入输出缓冲区。
每个线程产生 64 个值,我将它们写入输出缓冲区,这意味着输出缓冲区的大小为 (rows*cols*64)。 我还想支持最多支持 640 个值的计算,但显然每个线程都无法将 640 个值写入缓冲区,因为需要 VRAM。
我必须说线程写入不同的位置,没有覆盖,也就是说会有 64*number_of_threads = 64*global_id(0)*global_id(1) = 64*rows*cols 值。
这是我代码中的一个主要瓶颈,我的意思是写入 64 个值,我认为这与内存带宽有关,但我不太确定。
我该怎么做才能让每个线程高效地计算 64 个值并将其写入输出缓冲区?这不可能吗?
我的GPU是rx 480 4gb,我知道(rows*cols*64)大小有时候可能太大了,放不下VRAM,但是就算放好,写起来也慢,我觉得带宽很显卡高?
还有其他两个输出缓冲区,但它们的大小要小得多,所以我们可以忽略它们。
总而言之,这段代码的作用是 1) 读取一个9像素的方块,中间的一个是当前值。
2) 将 8 个邻居乘以当前值,我们得到每个像素的 8 个值。
3) 将 8 个邻居写入邻居缓冲区。
4) 将 8*8 值写入 Rx 缓冲区。此缓冲区“模拟” x_* x_^T 结果,即相邻值的 (8x1)x(1x8) 矩阵相乘。
请注意,我正在以“转置形式”写入输出缓冲区,即位置 (x,y) 处的每个线程在 (y,x)、(y+1,x) 处连续写入 64 个值。 ..(y+63,x) 这是因为:
1) 这是最快的方法!我写成 (x,y) -> (x+1,y),...(x+63,y) 的版本肯定更慢。
2) 我需要这种形式,因为我使用的是 ArrayFire 库,该库需要加载缓冲区,但它会以行优先顺序消耗缓冲区并将内容以列优先顺序放入其数组中,即这样就不需要转置数组(这将使用大量的 vram 副本)
【问题讨论】:
-
只要相邻线程执行合并写入并为 64 次写入中的每一次保持流水线,它应该没有瓶颈。
-
我已经阅读了关于合并读/写的信息,但我仍然不知道它是什么意思。我确定我不应该连续写入(就像我现在所做的那样)数据?我究竟应该怎么做?我贴了一些代码,你能看一下吗?谢谢!
-
Rx[counter + x_minus_pad_mul_64_mul_real_height + y_minus_pad_mul_64]有一个counter,它会停止合并写入。您需要为每个工作项跳过整个图像。所以他们把 image1 image2 image3 写成一个完整的内核。相邻线程需要连续的地址。如果每个工作项有 64 个空写入地址,则应将整个数据打包为单个数组,作为 image1 image2 image3 但不是 pixel1s pixel2s pixel3s ...。这种工作只会有利于 SSE AVX 类型指令,但对于 opencl 和 cuda 你会需要(大多数情况下)不与他人发生碰撞的距离 -
感谢您的反馈!
标签: multithreading gpu opencl simd