#include<stdio.h>
#include<cuda_runtime.h>
#include<sys/time.h>
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
double measure_time()
{
struct timeval tp;
gettimeofday(&tp,NULL);
return ((double)tp.tv_sec+(double)tp.tv_usec*1.e-6);
}
__global__ void sum_matrix(int *a,int *b,int *c,int nx,int ny)
{
int ix=blockIdx.x*blockDim.x+threadIdx.x;
int iy=blockIdx.y*blockDim.y+threadIdx.y;
int idx=iy*nx+ix;
c[idx]=a[idx]+b[idx];
}
int main(int argc, char *argv[])
{
int dimx=atoi(argv[1]);
int dimy=atoi(argv[2]);
int nx=4096;
int ny=4096;
dim3 block (dimx,dimy);
dim3 grid (nx/dimx,ny/dimy);
double start,end;
int *a,*b,*c;
long long nbytes=nx*ny*sizeof(int);
cudaMalloc((int**)&a,nbytes);
cudaMalloc((int**)&b,nbytes);
cudaMalloc((int**)&c,nbytes);
start=measure_time();
sum_matrix<<<grid,block>>>(a,b,c,nx,ny);
cudaDeviceSynchronize();
gpuErrchk( cudaPeekAtLastError() );
end=measure_time();
printf("Time elapsed = %f ms\n",(end-start)*1000);
cudaFree(a);
cudaFree(b);
cudaFree(c);
return 0;
}
上面是一个2d矩阵添加内核,我用它来检查MSI GTX 750 1 GB GDDR5卡上不同块大小配置的执行时间。以下是不同块大小配置的执行时间结果。
./ sum_matrix 32 32 已用时间= 3.028154毫秒
./ sum_matrix 32 16 经过的时间= 3.180981毫秒
./ sum_matrix 16 32 已用时间= 2.942085毫秒
./ sum_matrix 16 16 经过的时间= 3.238201毫秒
./ sum_matrix 64 8 已用时间= 3.020048毫秒
./ sum_matrix 64 16 经过的时间= 3.304005毫秒
./ sum_matrix 128 2 经过的时间= 2.965927毫秒
./ sum_matrix 128 1 经过的时间= 2.896070毫秒
./ sum_matrix 256 2 已用时间= 3.004074毫秒
./ sum_matrix 256 1 已用时间= 2.948046毫秒
我能理解的是,将块大小增加到最大(1024个线程),就像(64,16)的情况一样,可能会降低可用的并行性,从而表现更差。我没有得到的是为什么增加块x维度和减少块y提供更好的性能。是由于内存合并/缓存还是分歧?
由于
答案 0 :(得分:1)
我认为你的主要问题是差异在统计上并不具有统计意义。对于如此少量的数据,实际执行内核启动的开销很可能主导执行时间。请注意,无论使用的块大小如何,所有时间都在3 ms左右。
通过在循环中多次启动内核并平均执行时间,您可能会获得更精确的结果,但是通过这么小的内核调用,这可能只会用于确认所有启动都在执行由于启动和块调度开销导致实际内核执行时间占用的时间相同。
为了看到使用不同块大小的任何统计上显着的结果,你可能需要做一些(更多)更重要的事情而不仅仅是1600万个整数添加。
答案 1 :(得分:1)
首先,正如我在评论中所提到的,当游戏卡上的时间安排,特别是对于这样的短测试时,预计结果会出现波动。它们是动态时钟的,并且时钟不会是逐次运行的。
根据我的经验,块大小/形状不太可能对这种天真实现的元素问题产生太大影响。只要您的块x维度是32的倍数,并且您有足够大的块来获得100%的占用率。在那之后,它只是流入和流出数据。
但是,您可以比您的实施做得更好。 This reference现在变得相当老,但提出了一些好处。从本质上讲,计算每个线程的许多元素将为您提供更好的性能。
另一个(小的)性能改进将来自内存事务的矢量化。如果使用int4s而不是int,则硬件可以发出128字节的加载/存储指令,而不是32字节的加载/存储指令。由于您要处理的内存指令少4倍,因此效率稍高。