我正在使用CUDA实现积分图像计算模块以提高性能。 但它的速度比CPU模块慢。 请让我知道我做错了什么。 cuda内核和主机代码如下。 而且,另一个问题是...... 在内核SumH中,使用纹理内存比全局内容慢,imageTexture定义如下。
texture<unsigned char, 1> imageTexture;
cudaBindTexture(0, imageTexture, pbImage);
//内核水平和垂直扫描图像。
__global__ void SumH(unsigned char* pbImage, int* pnIntImage, __int64* pn64SqrIntImage, float rVSpan, int nWidth)
{
int nStartY, nEndY, nIdx;
if (!threadIdx.x)
{
nStartY = 1;
}
else
nStartY = (int)(threadIdx.x * rVSpan);
nEndY = (int)((threadIdx.x + 1) * rVSpan);
for (int i = nStartY; i < nEndY; i ++)
{
for (int j = 1; j < nWidth; j ++)
{
nIdx = i * nWidth + j;
pnIntImage[nIdx] = pnIntImage[nIdx - 1] + pbImage[nIdx - nWidth - i];
pn64SqrIntImage[nIdx] = pn64SqrIntImage[nIdx - 1] + pbImage[nIdx - nWidth - i] * pbImage[nIdx - nWidth - i];
//pnIntImage[nIdx] = pnIntImage[nIdx - 1] + tex1Dfetch(imageTexture, nIdx - nWidth - i);
//pn64SqrIntImage[nIdx] = pn64SqrIntImage[nIdx - 1] + tex1Dfetch(imageTexture, nIdx - nWidth - i) * tex1Dfetch(imageTexture, nIdx - nWidth - i);
}
}
}
__global__ void SumV(unsigned char* pbImage, int* pnIntImage, __int64* pn64SqrIntImage, float rHSpan, int nHeight, int nWidth)
{
int nStartX, nEndX, nIdx;
if (!threadIdx.x)
{
nStartX = 1;
}
else
nStartX = (int)(threadIdx.x * rHSpan);
nEndX = (int)((threadIdx.x + 1) * rHSpan);
for (int i = 1; i < nHeight; i ++)
{
for (int j = nStartX; j < nEndX; j ++)
{
nIdx = i * nWidth + j;
pnIntImage[nIdx] = pnIntImage[nIdx - nWidth] + pnIntImage[nIdx];
pn64SqrIntImage[nIdx] = pn64SqrIntImage[nIdx - nWidth] + pn64SqrIntImage[nIdx];
}
}
}
//主机代码
int nW = image_width;
int nH = image_height;
unsigned char* pbImage;
int* pnIntImage;
__int64* pn64SqrIntImage;
cudaMallocManaged(&pbImage, nH * nW);
// assign image gray values to pbimage
cudaMallocManaged(&pnIntImage, sizeof(int) * (nH + 1) * (nW + 1));
cudaMallocManaged(&pn64SqrIntImage, sizeof(__int64) * (nH + 1) * (nW + 1));
float rHSpan, rVSpan;
int nHThreadNum, nVThreadNum;
if (nW + 1 <= 1024)
{
rHSpan = 1;
nVThreadNum = nW + 1;
}
else
{
rHSpan = (float)(nW + 1) / 1024;
nVThreadNum = 1024;
}
if (nH + 1 <= 1024)
{
rVSpan = 1;
nHThreadNum = nH + 1;
}
else
{
rVSpan = (float)(nH + 1) / 1024;
nHThreadNum = 1024;
}
SumH<<<1, nHThreadNum>>>(pbImage, pnIntImage, pn64SqrIntImage, rVSpan, nW + 1);
cudaDeviceSynchronize();
SumV<<<1, nVThreadNum>>>(pbImage, pnIntImage, pn64SqrIntImage, rHSpan, nH + 1, nW + 1);
cudaDeviceSynchronize();
答案 0 :(得分:2)
关于当前问题中的代码。我想提及两件事:启动参数和时序方法。
启动内核时,有两个主要参数指定要启动的线程数。它们位于<<<
和>>>
部分之间,是网格中的块数,以及每个块的线程数,如下所示:
foo <<< numBlocks, numThreadsPerBlock >>> (args);
要使单个内核在当前GPU上高效,您可以使用numBlocks * numThreadsPerBlock应至少为10,000的经验法则。 IE浏览器。 10,000件工作。这是一个经验法则,因此只有5,000个线程可以获得良好的结果(它随GPU而变化:更便宜的GPU可以用更少的线程消失),但这是您需要将其视为最小值的数量级。 你正在运行1024个线程。这几乎肯定是不够的(提示:内核中的循环看起来像扫描原型,这些可以并行完成)。
除此之外,还有一些其他事项需要考虑。
我不打算在这里深入探讨,但要确保你的时机安稳。 GPU代码往往具有一次性初始化延迟。如果这在您的时间范围内,您将看到错误的大运行时代码,旨在表示更大的代码。同样,CPU和GPU之间的数据传输也需要时间。在实际的应用程序中,您只能对数千次内核调用执行此操作,但在测试应用程序中,您可以在每次内核启动时执行一次。
如果您想获得准确的时间安排,您必须使您的示例更能代表最终代码,或者您必须确保只对将要重复的区域进行计时。
答案 1 :(得分:0)
使用CUDA时,您应该记住一些事项。
答案 2 :(得分:0)
上面提到的GTX750有512个CUDA核心(这些与着色器单元相同,只是以/不同/模式驱动)。 http://www.nvidia.de/object/geforce-gtx-750-de.html#pdpContent=2
创建整体图像的任务只能部分地并行化,因为结果数组中的任何结果值都取决于它的前辈们。此外,每次内存传输只是一个微小的数学部分,因此ALU供电,因此不可避免的内存传输可能是瓶颈。这样的加速器可能提供一些加速,但不是一个惊心动魄的加速,因为责任本身不允许它。
如果您要在同一输入数据上计算多个积分图像变体,您将能够看到&#34;刺激&#34;更可能是由于更高的并行选项和更高的数学运算量。但那将是一个不同的职责。
答案 3 :(得分:-1)
唯一可以确定的方法是对代码进行分析,但在这种情况下,我们可能会做出合理的猜测。
您基本上只是对一些数据执行单次扫描,并对每个项目执行极少的处理。
鉴于您对每个项目进行的处理很少,使用CPU处理数据时的瓶颈可能只是从内存中读取数据。
当您在GPU上进行处理时,仍需要从内存中读取数据并将其复制到GPU的内存中。这意味着我们仍然需要从主内存中读取所有数据,就像CPU进行处理一样。更糟糕的是,这一切都必须写入GPU的内存,导致进一步放缓。当GPU甚至开始进行实际处理时,你已经耗费了比CPU完成工作所花费的更多时间。
对于Cuda来说,您通常需要对每个单独的数据项进行更多处理。在这种情况下,CPU大部分时间可能已经几乎处于空闲状态,等待内存中的数据。在这种情况下,GPU不太可能提供很多帮助除非输入数据已经存在于GPU的内存中,因此GPU可以在没有任何额外复制的情况下进行处理。