我是Cuda编程的新手,我有一个将RGB图像转换为Greyscale的代码。已经提供了用于读取像素的RGB值并将它们转换为GreyScale的算法。 并行化代码使我的速度提高了大约40-50倍。我想进一步优化它以实现大约100倍的加速。为此,我希望使用共享内存访问的速度比全局内存访问快。我已经浏览了不同的在线资源,并对共享内存访问有基本的了解。但在我的代码中,我遇到了解如何实现共享内存的问题,读取RGB值和转换为灰度的代码
for ( int y = 0; y < height; y++ ) {
for ( int x = 0; x < width; x++ ) {
float grayPix = 0.0f;
float r = static_cast< float >(inputImage[(y * width) + x]);
float g = static_cast< float >(inputImage[(width * height) + (y * width) + x]);
float b = static_cast< float >(inputImage[(2 * width * height) + (y * width) + x]);
grayPix = ((0.3f * r) + (0.59f * g) + (0.11f * b));
grayPix = (grayPix * 0.6f) + 0.5f;
darkGrayImage[(y * width) + x] = static_cast< unsigned char >(grayPix);
}
}
输入图像char *,我们使用CImg库来操作图像
CImg< unsigned char > inputImage = CImg< unsigned char >(argv[1]);
用户在运行代码时将图像路径作为参数传递给
这是我对它的Cuda实施
unsigned int y = (blockIdx.x * blockDim.x) + threadIdx.x;
unsigned int x = (blockIdx.y * blockDim.y) + threadIdx.y;
float grayPix = 0.0f;
float r = static_cast< float >(inputImage[(y * height) + x]);
float g = static_cast< float >(inputImage[(width * height) + (y * height) + x]);
float b = static_cast< float >(inputImage[(2 * width * height) + (y * height) + x]);
grayPix = ((0.3f * r) + (0.59f * g) + (0.11f * b));
grayPix = (grayPix * 0.6f) + 0.5f;
darkGrayImage[(y * height) + x] = static_cast< unsigned char >(grayPix);
网格并阻止并调用代码
dim3 gridSize(width/16,height/16);
dim3 blockSize(16,16);
greyScale<<< gridSize, blockSize >>>(width,height,d_in, d_out);
其中width和height是输入图像的宽度和高度。我试过块大小为(32,32),但它减慢了代码而不是加速它
现在我想添加共享内存但问题是输入变量InputImage的访问是非线性的,所以我要添加到共享内存中的值是什么 我试过像
这样的东西 unsigned int y = (blockIdx.x * blockDim.x) + threadIdx.x;
unsigned int x = (blockIdx.y * blockDim.y) + threadIdx.y;
extern __shared__ int s[];
s[x]=inputImage[x];
__syncthreads();
然后在实现中用s替换inputImage但是输出错误(全黑图像) 你能帮助我在这里了解如何实现共享内存,即使它是可能的和有用的,有没有办法让我以更加合并的方式进行访问?
任何帮助都会感激不尽
答案 0 :(得分:2)
由于以下几个原因,这不起作用:
unsigned int x = (blockIdx.y * blockDim.y) + threadIdx.y;
extern __shared__ int s[];
s[x]=inputImage[x];
一个原因是我们不能使用全局索引(x
)作为共享内存索引,除非数据集足够小以适应共享内存。对于尺寸相当大的图像,您无法将整个图像放入共享内存的单个实例中。此外,您只使用二维数据集的一维索引(x),因此这可能没有意义。
这表明对如何在程序中使用共享内存的普遍缺乏了解。然而,我们可以观察到,对于正确编写的RGB-&gt;灰度代码,共享内存使用不太可能提供任何好处,而不是试图对其进行排序。
共享内存带宽优势(当您说“速度更快”时,您指的是这一点)在数据重用时非常有用。 RGB->灰度代码不需要重复使用任何数据。您只需从全局内存中加载一次R,G,B数量,并将计算出的灰度数量恰好存储在全局内存中一次。暂时将某些数据移动到共享内存不会加快速度。您仍然需要执行全局加载和全局存储,并且对于正确编写的代码,这应该是所有必要的。
但是在您的问题中,您已经建议了一条可能的改进路径:合并访问。如果您要对发布的代码进行概要分析,您会发现完全未合并的访问模式。为了实现良好的合并,我们希望复合索引计算具有threadIdx.x
变量不会乘以任何内容的属性:
unsigned int y = (blockIdx.x * blockDim.x) + threadIdx.x;
unsigned int x = (blockIdx.y * blockDim.y) + threadIdx.y;
float grayPix = 0.0f;
float r = static_cast< float >(inputImage[(y * height) + x]);
^
|
y depends on threadIdx.x
但在您的情况下,您的索引计算会将threadIdx.x
乘以height
。这将导致非合并访问。 warp中的相邻线程将具有不同的threadIdx.x
,并且我们希望warp中相邻线程的索引计算导致内存中的相邻位置,以实现良好的合并访问。如果将threadIdx.x
乘以任何东西,则无法实现此目的。
这个问题的解决方案非常简单。您应该使用与您显示的非CUDA代码几乎完全相同的内核代码,并使用x
和y
的适当定义:
unsigned int x = (blockIdx.x * blockDim.x) + threadIdx.x;
unsigned int y = (blockIdx.y * blockDim.y) + threadIdx.y;
if ((x < width) && (y < height)){
float grayPix = 0.0f;
float r = static_cast< float >(inputImage[(y * width) + x]);
float g = static_cast< float >(inputImage[(width * height) + (y * width) + x]);
float b = static_cast< float >(inputImage[(2 * width * height) + (y * width) + x]);
grayPix = ((0.3f * r) + (0.59f * g) + (0.11f * b));
grayPix = (grayPix * 0.6f) + 0.5f;
darkGrayImage[(y * width) + x] = static_cast< unsigned char >(grayPix);
}
当然,这不是一个完整的代码。你没有显示完整的代码,所以如果你回答“我试过这个但它不起作用”,我不太可能帮助你,因为我不知道你实际运行的代码是什么。但是:
请注意,“它不起作用”的响应意味着您确实要求调试帮助,而不是概念性解释,在这种情况下,您supposed to提供MCVE。你所展示的不是MCVE。优选地,您的MCVE不应该依赖于像CImg这样的外部库,这意味着您需要努力创建一个独立测试,但是要证明您遇到的问题。
此外,我建议您在使用CUDA代码时遇到问题,使用proper CUDA error checking以及使用cuda-memcheck
运行代码。
(正确的CUDA错误检查会发现您尝试使用共享内存时出现问题,例如,由于共享内存中的越界索引。)