我是一名学习Cuda的学生,我想优化我的核心功能的执行时间。结果,我实现了一个计算两张图片之间差异的短程序。所以我比较了C中的经典CPU执行和Cuda C中的GPU执行之间的执行时间。
在这里,您可以找到我正在谈论的代码:
int *imgresult_data = (int *) malloc(width*height*sizeof(int));
int size = width*height;
switch(computing_type)
{
case GPU:
HANDLE_ERROR(cudaMalloc((void**)&dev_data1, size*sizeof(unsigned char)));
HANDLE_ERROR(cudaMalloc((void**)&dev_data2, size*sizeof(unsigned char)));
HANDLE_ERROR(cudaMalloc((void**)&dev_data_res, size*sizeof(int)));
HANDLE_ERROR(cudaMemcpy(dev_data1, img1_data, size*sizeof(unsigned char), cudaMemcpyHostToDevice));
HANDLE_ERROR(cudaMemcpy(dev_data2, img2_data, size*sizeof(unsigned char), cudaMemcpyHostToDevice));
HANDLE_ERROR(cudaMemcpy(dev_data_res, imgresult_data, size*sizeof(int), cudaMemcpyHostToDevice));
float time;
cudaEvent_t start, stop;
HANDLE_ERROR( cudaEventCreate(&start) );
HANDLE_ERROR( cudaEventCreate(&stop) );
HANDLE_ERROR( cudaEventRecord(start, 0) );
for(int m = 0; m < nb_loops ; m++)
{
diff<<<height, width>>>(dev_data1, dev_data2, dev_data_res);
}
HANDLE_ERROR( cudaEventRecord(stop, 0) );
HANDLE_ERROR( cudaEventSynchronize(stop) );
HANDLE_ERROR( cudaEventElapsedTime(&time, start, stop) );
HANDLE_ERROR(cudaMemcpy(imgresult_data, dev_data_res, size*sizeof(int), cudaMemcpyDeviceToHost));
printf("Time to generate: %4.4f ms \n", time/nb_loops);
break;
case CPU:
clock_t begin = clock(), diff;
for (int z=0; z<nb_loops; z++)
{
// Apply the difference between 2 images
for (int i = 0; i < height; i++)
{
tmp = i*imgresult_pitch;
for (int j = 0; j < width; j++)
{
imgresult_data[j + tmp] = (int) img2_data[j + tmp] - (int) img1_data[j + tmp];
}
}
}
diff = clock() - begin;
float msec = diff*1000/CLOCKS_PER_SEC;
msec = msec/nb_loops;
printf("Time taken %4.4f milliseconds", msec);
break;
}
这是我的核心功能:
__global__ void diff(unsigned char *data1 ,unsigned char *data2, int *data_res)
{
int row = blockIdx.x;
int col = threadIdx.x;
int v = col + row*blockDim.x;
if (row < MAX_H && col < MAX_W)
{
data_res[v] = (int) data2[v] - (int) data1[v];
}
}
我为每个人获得了这些执行时间
我想知道为什么GPU结果不会像它应该的那样低。我是Cuda的初学者,所以如果有一些经典错误请全面。
EDIT1: 感谢您的反馈意见。我试图删除&#39; if&#39;来自内核的条件,但它并没有改变我的程序执行时间。
然而,在安装了Cuda profiler之后,它告诉我我的线程并没有同时运行。我不明白为什么会有这种消息,但这似乎是正确的,因为我使用GPU的速度比使用CPU快5到6倍。该比率应该更大,因为每个线程应该同时处理一个像素到所有其他像素。如果你知道我做错了什么,那就太好了......
流量。
答案 0 :(得分:-2)
代码可能存在其他问题,但这就是我所看到的。 __global__ void diff
中的以下行被认为不是最佳的:
if (row < MAX_H && col < MAX_W)
{
data_res[v] = (int) data2[v] - (int) data1[v];
}
内核中的条件运算符会导致 warp divergence 。这意味着warp中的if
和else
部分按顺序执行,而不是并行执行。另外,正如您可能已经意识到的那样,if
仅在边界处评估为false
。为了避免分歧和不必要的计算,将图像分成两部分:
row < MAX_H && col < MAX_W
始终为true
的中心部分。为此区域创建其他内核。此处不需要if
。
将使用您的diff
内核的边框区域。
显然,你将修改调用内核的代码。
另外注意:
GPU具有面向吞吐量的架构,但不像CPU那样面向延迟。这意味着在处理少量数据时,CPU可能比CUDA更快。您是否尝试过使用大型数据集?
CUDA Profiler是一个非常方便的工具,可以告诉你在代码中不是最佳的。
答案 1 :(得分:-2)
我认为你没有正确测量时间,内存复制在GPU中是一个耗时的步骤,在测量你的时间时你应该考虑到这一步。
我看到了一些你可以测试的细节:
我认为您使用MAX_H和MAX_H作为常量,您可以考虑使用cudaMemcpyToSymbol()。
请记住使用__syncthreads()同步线程,这样就不会在每次循环迭代之间出现问题。
CUDA适用于warp,因此每个块的块和线程数更好地作为8的倍数,但每块不超过512个线程,除非您的硬件支持它。以下是每个块使用128个线程的示例:&lt;&lt;&lt;(cols * rows + 127)/ 128,128&gt;&gt;&gt;。
请记住,在GPU中释放已分配的内存并销毁创建的时间事件。
在你的内核函数中,你可以有一个变量int v = threadIdx.x + blockIdx.x * blockDim.x。
您是否在执行时间旁边测试过您的结果是否正确?我认为你应该在使用填充时使用cudaMallocPitch()和cudaMemcpy2D()。