我对CUDA很新,并试图编写一个测试程序。 我在GeForce GT 520卡上运行该应用程序,并且性能非常差。
应用程序用于处理某些图像,每行由一个单独的线程处理。 以下是该应用程序的简化版本。请注意,在实际应用程序中,所有常量实际上都是变量,作为调用者提供。
运行以下代码时,超过20秒才能完成执行。
但与使用 malloc / free 相反,当l_SrcIntegral
被定义为本地数组时(如注释行中所示),需要不到1秒以完成执行。
由于数组的实际大小是动态的(而不是1700),因此无法在实际应用程序中使用此本地数组。
任何建议如何改善这个相当简单的代码的性能将不胜感激。
#include "cuda_runtime.h"
#include <stdio.h>
#define d_MaxParallelRows 320
#define d_MinTreatedRow 5
#define d_MaxTreatedRow 915
#define d_RowsResolution 1
#define k_ThreadsPerBlock 64
__global__ void myKernel(int Xi_FirstTreatedRow)
{
int l_ThreadIndex = blockDim.x * blockIdx.x + threadIdx.x;
if (l_ThreadIndex >= d_MaxParallelRows)
return;
int l_Row = Xi_FirstTreatedRow + (l_ThreadIndex * d_RowsResolution);
if (l_Row <= d_MaxTreatedRow) {
//float l_SrcIntegral[1700];
float* l_SrcIntegral = (float*)malloc(1700 * sizeof(float));
for (int x=185; x<1407; x++) {
for (int i=0; i<1700; i++)
l_SrcIntegral[i] = i;
}
free(l_SrcIntegral);
}
}
int main()
{
cudaError_t cudaStatus;
cudaStatus = cudaSetDevice(0);
int l_ThreadsPerBlock = k_ThreadsPerBlock;
int l_BlocksPerGrid = (d_MaxParallelRows + l_ThreadsPerBlock - 1) / l_ThreadsPerBlock;
int l_FirstRow = d_MinTreatedRow;
while (l_FirstRow <= d_MaxTreatedRow) {
printf("CUDA: FirstRow=%d\n", l_FirstRow);
fflush(stdout);
myKernel<<<l_BlocksPerGrid, l_ThreadsPerBlock>>>(l_FirstRow);
cudaDeviceSynchronize();
l_FirstRow += (d_MaxParallelRows * d_RowsResolution);
}
printf("CUDA: Done\n");
return 0;
}
答案 0 :(得分:1)
<强> 1 强>
正如@aland所说,你甚至可能会遇到更糟糕的性能,只计算每个内核调用中的一行。
你必须考虑处理整个输入,只是理论上使用大规模并行处理的力量。
为什么只用320个线程启动多个内核来计算一行? 如何使用尽可能多的块,并使每个块的线程处理一行。
(每个区块320个线程不是一个好选择,看看如何达到更好的占用率)
<强> 2 强>
如果作为寄存器和共享内存的快速资源不够,则必须使用tile apporach,这是使用GPGPU编程的基础之一。
将输入数据分成相同大小的切片,并在线程中循环处理它们。
在这里,我发布了一个这样的平铺方法的例子:
Parallelization in CUDA, assigning threads to each column
请注意该平铺方法中的范围检查!
为您提供想法的示例:
以任意大小的矩阵计算列向量中所有元素的总和。
每个块处理一列,并且该块的线程存储在一个tile中,它们的元素在共享内存数组中循环。完成后,他们使用平行减少计算总和,只是为了开始下一次迭代 最后,每个块计算出其向量的总和。
答案 1 :(得分:0)
您仍然可以使用共享内存来使用动态数组大小。只需在内核调用的<<<...>>>
中传递第三个参数。这是每个块的共享内存大小。
一旦你在那里,只需将所有相关数据带入你的共享数组(你仍然应该尝试保持合并访问),为每个线程带来一个或几个(如果它与保持合并访问相关)元素。在线程被带到后同步(只有当你需要停止竞争条件,确保整个数组在完成任何计算之前都在共享内存中)并且你很高兴。
另外:你应该使用块和线程进行细分,而不是循环。我理解这只是一个使用本地数组的例子,但是,它仍然可以通过块/线程进行细分而不是嵌套for循环(这对于性能非常糟糕!)我希望你只使用1个块来运行示例代码和1个线程,否则没有多大意义。