Memory 毫无意义地使用CUDA共享内存

Memory 毫无意义地使用CUDA共享内存,memory,cuda,shared,convolution,Memory,Cuda,Shared,Convolution,我有两个相同算法的版本。它最初是卷积运算,但我对其进行了修改,将其简化为卷积运算,以检查我的瓶颈在哪里(请注意,每个循环只有一次全局内存访问): 而我的设备的信息是: Name: GeForce GTX 660 Ti Minor Compute Capability: 0 Major Compute Capability: 3 Warp Size: 32 Max Treads per Block: 1024 Max Threads Dimension: (1024,1024,64) Max Gr

我有两个相同算法的版本。它最初是卷积运算,但我对其进行了修改,将其简化为卷积运算,以检查我的瓶颈在哪里(请注意,每个循环只有一次全局内存访问):

而我的设备的信息是:

Name: GeForce GTX 660 Ti
Minor Compute Capability: 0
Major Compute Capability: 3
Warp Size: 32
Max Treads per Block: 1024
Max Threads Dimension: (1024,1024,64)
Max Grid Size: (2147483647,65535,65535)
Number of SM: 7
Max Threads Per SM: 2048
Regs per Block (SM): 65536
Total global Memory: 2146762752
Shared Memory per Block: 49152
有没有人对这一糟糕的进步有任何暗示?我不知道还有谁可以问

编辑: 我今天使用的是另一种nvidia卡,因为我无法访问实验室。它还具有计算能力3.0。 我把这两个if语句都排除在循环之外。 我正在使用-arch compute\u 30-代码sm\u 30进行编译 我移除所有铸件。 全局矩阵现在声明为const unsigned char*restrictMd 这次我使用了一个9x9过滤器,它可以让每个像素在进入共享内存后重复使用81次

我从终端获得3138.41毫秒(全局版本)和3120.96毫秒(共享版本)。 在可视探查器中,需要更长的时间。这是我得到的(截图)

虽然我迷路了

请在这里找到易于编译和执行的算法:

./用于全局内存版本的卷积8000 4000 159 9 edge_detection_9.txt 0
./convolution 8000 4000 159 9 edge_detection_9.txt 1用于共享内存版本

引起我注意的第一件事:

ptxas info: Compiling entry function '_Z8convolvePhPfS_iiiii' for 'sm_10'
您的卡具有计算能力3.0,因此您应该使用sm_30进行编译。sm_10缺少sm_30的许多功能,共享内存更小,寄存器更少


接下来我要做的是将if语句放在for循环之外的两个内核中,以便进行适当的内核比较


接下来,我将增加内核大小以突出共享内存的影响。内核中只有9次访问(如果我计算正确的话),这意味着:

  • 在第一个内核中,将全局内存中的元素直接读入寄存器并使用它们
  • 在第二个内核中,每个线程从全局内存中读取一个元素,每个线程在共享缓存中进行9次访问
  • 由于没有大量重用共享缓存中的元素,因此访问全局内存的代价太大
此外,
sum+=(int)((float)Mds[局部像素+x+y*Mds_宽度]在共享缓存中生成一些银行冲突,从而降低其吞吐量

如果内核大小始终为3,则还可以通过展开for循环并使用固定索引来替换for循环,以帮助编译器


我还担心从uchar到float再到int的施法惩罚。我知道这些操作成本很高,进一步降低了共享缓存的使用收益。例如,你为什么要施放
(int)sum因为Rd是无符号字符?为什么不将Rd声明为int*


我发现您的内核中也需要Kd。正如现在声明的那样,它存储在全局内存中。如果它只是一个3x3过滤器,您可以对其进行硬编码,或者将其加载到线程局部变量的循环外部,这样就有可能存储到寄存器中

如果这不起作用,您可以尝试将系数存储到共享内存中。实际上是复制系数。每个线程都有一个线程,这样可以避免银行冲突。共享内存在开普勒上有32个端口,这样可以同时在warp中的所有线程上提取系数


总之我认为您的共享缓存内核付出了访问全局内存、共享内存库冲突、使用sm_10和多类型强制转换的代价,从而大大降低了共享缓存的收益。一个一般性建议是,使用CUDA视觉探查器验证这些点



此外,我还将尝试使用纹理缓存,方法是将Md声明为
const\uuuu restrict\uuu
。与全局内存访问相比,这可能会显示出一些加速,因为这是一个多端口缓存,带有一个特殊的映射,旨在减少银行冲突。事实上,我希望这比共享内存的情况更有效。

首先引起我注意的是:

ptxas info: Compiling entry function '_Z8convolvePhPfS_iiiii' for 'sm_10'
您的卡具有计算能力3.0,因此您应该使用sm_30进行编译。sm_10缺少sm_30的许多功能,共享内存更小,寄存器更少


接下来我要做的是将if语句放在for循环之外的两个内核中,以便进行适当的内核比较


接下来,我将增加内核大小以突出共享内存的影响。内核中只有9次访问(如果我计算正确的话),这意味着:

  • 在第一个内核中,将全局内存中的元素直接读入寄存器并使用它们
  • 在第二个内核中,每个线程从全局内存中读取一个元素,每个线程在共享缓存中进行9次访问
  • 由于没有大量重用共享缓存中的元素,因此访问全局内存的代价太大
此外,
sum+=(int)((float)Mds[局部像素+x+y*Mds_宽度]在共享缓存中生成一些银行冲突,从而降低其吞吐量

如果内核大小始终为3,则还可以通过展开for循环并使用固定索引来替换for循环,以帮助编译器


我还担心从uchar到float再到int的施法惩罚。我知道这些操作成本很高,进一步降低了共享缓存的使用收益。例如,你为什么要施放
(int)sum因为Rd是无符号字符?为什么不将Rd声明为int*


我发现您的内核中也需要Kd。正如现在声明的那样,它存储在全局内存中。如果它只是一个3x3过滤器,您可以对其进行硬编码,或者将其加载到线程局部变量的循环外部,这样就有可能存储到寄存器中。

ptxas info: 560 bytes gmem, 52 bytes cmem[14] ptxas info: Compiling entry function '_Z8convolvePhPfS_iiiii' for 'sm_10' ptxas info: Used 16 registers, 384 bytes smem, 4 bytes cmem[1]
Name: GeForce GTX 660 Ti
Minor Compute Capability: 0
Major Compute Capability: 3
Warp Size: 32
Max Treads per Block: 1024
Max Threads Dimension: (1024,1024,64)
Max Grid Size: (2147483647,65535,65535)
Number of SM: 7
Max Threads Per SM: 2048
Regs per Block (SM): 65536
Total global Memory: 2146762752
Shared Memory per Block: 49152
ptxas info: Compiling entry function '_Z8convolvePhPfS_iiiii' for 'sm_10'