将数据上传到共享内存中以用于卷积内核

时间:2014-01-27 12:10:18

标签: cuda gpu

我在理解批量加载方面遇到一些困难,如引用的评论中所述。为了计算像素中的卷积,大小为5的掩模必须以该特定像素为中心。图像被分为图块。应用卷积蒙版后的这些图块是最终输出图块,其大小为TILE_WIDTH*TILE_WIDTH。对于属于输出图块边框的像素,当此图块属于图像的边框时,图像必须从相邻图块借用一些像素。否则,这些借来的值被赋值为零。

中描述了这两个步骤
if (srcY >= 0 && srcY < height && srcX >= 0 && srcX < width)
    N_ds[destY][destX] = I[src];
else
    N_ds[destY][destX] = 0;

因此,共享内存阵列的每一侧都有TILE_WIDTH + Mask_width - 1个维度。我不清楚代码的以下部分。

  1. destYdestX索引。 将输出索引除以输入切片宽度意味着什么?
  2. srcY添加srcX索引。 为什么destYdestX索引参与srcY添加srcX索引?

    srcY = blockIdx.y * TILE_WIDTH + destY - Mask_radius;

    srcX = blockIdx.x * TILE_WIDTH + destX - Mask_radius;

  3. 为什么在第二次加载时我们使用偏移TILE_WIDTH * TILE_WIDTH
  4. 一般来说,有两次装载的直观解释是什么?
  5. 所有这些问题都可以跟随基于图像的直观示例吗?
  6. 谢谢!
  7. 编辑:已添加图片。在绿色中有输出瓦片,在红色中我们有掩模以114索引为中心。很明显,面具借用了不同瓷砖的元素。 最后,此图像指的是一个频道。

    示例:根据下图,我试图写一个例子。输出图块具有基于blockIdx.x=1blockIdx.y=1的{​​{1}}和destY=0。也, destX=0srcY = 1*6+0-3=3srcX = 3。根据计算和图像示例,我们没有匹配。在第一个共享内存位置中,应存储的值是全局索引src = (3*18+3)*3+0=171。上述计算有什么问题?有人可以帮忙吗?

    enter image description here

    57

1 个答案:

答案 0 :(得分:7)

您的问题在概念上类似于我在StackOverflow上的第一个问题:Moving a (BS_X+1)(BS_Y+1) global memory matrix by BS_XBS_Y threads

您遇到以下问题:大小为TILE_WIDTHxTILE_WIDTH的每个线程块应填充大小为(TILE_WIDTH + Mask_width - 1)x(TILE_WIDTH + Mask_width - 1) 的共享内存区域。

  

4)一般来说,有两次加载的直观解释是什么?

由于共享内存区(TILE_WIDTH + Mask_width - 1)x(TILE_WIDTH + Mask_width - 1)大于块大小TILE_WIDTHxTILE_WIDTH并假设它小于2xTILE_WIDTHxTILE_WIDTH,因此每个线程最多应将两个元素从全局内存移动到共享记忆。这就是你加载两个阶段的原因。

  

1)destYdestX索引。将输出索引除以输入切片宽度意味着什么?

这涉及第一个加载阶段,它被指定从全局内存加载TILE_WIDTHxTILE_WIDTH个元素并填充共享内存区域的最上部分。

所以,操作

dest = threadIdx.y * TILE_WIDTH + threadIdx.x;

时展平通用线程的2D坐标
destX = dest % w;
destY = dest / w; 

进行逆运算,因为它计算通用线程相对于共享内存区域的2D坐标。

  

2)srcY添加srcX索引。为什么destYdestX索引参与srcY添加srcX索引?

srcY = blockIdx.y * TILE_WIDTH + destY - Mask_radius;

srcX = blockIdx.x * TILE_WIDTH + destX - Mask_radius;
如果块大小和共享内存大小相同,则

(blockIdx.x * TILE_WIDTH, blockIdx.y * TILE_WIDTH)将是全局内存位置的坐标。由于您也是从neighboor tile中“借用”内存值,因此您必须将上述坐标移动(destX - Mask_radius, destY - Mask_radius)

  

3)为什么在第二次加载时我们使用偏移量TILE_WIDTH * TILE_WIDTH?

您有此偏移量,因为在第一个内存阶段,您已经填充了共享内存的“第一个”TILE_WIDTHxTILE_WIDTH位置。

修改

下图说明了扁平线程索引dest与共享内存位置之间的对应关系。在图片中,蓝色框表示通用图块的元素,而红色框表示邻居图块的元素。蓝色和红色框的并集对应于整个共享内存位置。如您所见,线程块的所有256个线程都涉及在绿线上方填充共享内存的上半部分,而填充共享的下半部分只涉及145记忆在绿线下面。现在您还应该了解TILE_WIDTH x TILE_WIDTH偏移量。

请注意,由于特定的参数选择,每个线程最多有2个内存加载。例如,如果你有TILE_WIDTH = 8,那么线程块中的线程数是64,而共享内存大小是12x12=144,这意味着每个线程负责执行自2以来至少 144/64=2.25共享内存写入。

enter image description here