具有可变块大小的CUDA矩阵加法定时 #包括 #包括 #包括 #定义gpuerchk(ans){gpuAssert((ans),_文件_,_行__)} 内联void gpuAssert(cudaError\u t代码,const char*文件,int行,bool abort=true) { 如果(代码!=cudaSuccess) { fprintf(标准,“GPUassert:%s%s%d\n”,cudaGetErrorString(代码)、文件、行); 如果(中止)退出(代码); } } 双重测量时间() { 结构timeval-tp; gettimeofday(&tp,NULL); 返回((双)tp.tv_-sec+(双)tp.tv_-usec*1.e-6); } __全局无效和矩阵(int*a,int*b,int*c,int nx,int ny) { intix=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块(dimx,dimy); dim3网格(nx/dimx,ny/dimy); 双起点,双终点; int*a、*b、*c; 长n字节=nx*ny*sizeof(int); Cudamaloc((国际**)和a,十亿字节); Cudamaloc((国际**)和b,N字节); Cudamaloc((国际**)和c,N字节); 开始=测量时间(); 求和矩阵(a,b,c,nx,ny); cudaDeviceSynchronize(); gpuerchk(cudaPeekAtLastError()); 结束=测量时间(); printf(“经过的时间=%f ms\n”,(结束-开始)*1000); cudaFree(a); 库达弗里(b); cudaFree(c); 返回0; }

具有可变块大小的CUDA矩阵加法定时 #包括 #包括 #包括 #定义gpuerchk(ans){gpuAssert((ans),_文件_,_行__)} 内联void gpuAssert(cudaError\u t代码,const char*文件,int行,bool abort=true) { 如果(代码!=cudaSuccess) { fprintf(标准,“GPUassert:%s%s%d\n”,cudaGetErrorString(代码)、文件、行); 如果(中止)退出(代码); } } 双重测量时间() { 结构timeval-tp; gettimeofday(&tp,NULL); 返回((双)tp.tv_-sec+(双)tp.tv_-usec*1.e-6); } __全局无效和矩阵(int*a,int*b,int*c,int nx,int ny) { intix=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块(dimx,dimy); dim3网格(nx/dimx,ny/dimy); 双起点,双终点; int*a、*b、*c; 长n字节=nx*ny*sizeof(int); Cudamaloc((国际**)和a,十亿字节); Cudamaloc((国际**)和b,N字节); Cudamaloc((国际**)和c,N字节); 开始=测量时间(); 求和矩阵(a,b,c,nx,ny); cudaDeviceSynchronize(); gpuerchk(cudaPeekAtLastError()); 结束=测量时间(); printf(“经过的时间=%f ms\n”,(结束-开始)*1000); cudaFree(a); 库达弗里(b); cudaFree(c); 返回0; },cuda,gpu,Cuda,Gpu,上面是一个2d矩阵加法内核,我用它来检查MSI GTX 750 1 GB GDDR5卡上不同块大小配置的执行时间。以下是不同块大小配置的执行时间结果 /求和矩阵32 经过的时间=3.028154毫秒 /求和矩阵32 16 经过的时间=3.180981毫秒 /求和矩阵16 32 经过的时间=2.942085毫秒 /求和矩阵16 经过的时间=3.238201毫秒 /求和矩阵64 8 经过的时间=3.020048毫秒 /求和矩阵64 16 经过的时间=3.304005毫秒 /求和矩阵128 2 经过的

上面是一个2d矩阵加法内核,我用它来检查MSI GTX 750 1 GB GDDR5卡上不同块大小配置的执行时间。以下是不同块大小配置的执行时间结果

/求和矩阵32 经过的时间=3.028154毫秒

/求和矩阵32 16 经过的时间=3.180981毫秒

/求和矩阵16 32 经过的时间=2.942085毫秒

/求和矩阵16 经过的时间=3.238201毫秒

/求和矩阵64 8 经过的时间=3.020048毫秒

/求和矩阵64 16 经过的时间=3.304005毫秒

/求和矩阵128 2 经过的时间=2.965927毫秒

/求和矩阵128 1 经过的时间=2.896070毫秒

/求和矩阵256 2 经过的时间=3.004074毫秒

/求和矩阵256 1 经过的时间=2.948046毫秒

我能理解的是,将块大小增加到最大值(1024个线程),就像(64,16)的情况一样,可能会降低可用的并行性,因此执行效果会更差。我不明白为什么增加块x维度和减少块y可以提供更好的性能。这是由于内存合并/缓存还是发散


谢谢

我想你这里的主要问题是,这些差异一开始在统计学上并不显著。对于如此少量的数据,实际执行内核启动的开销很可能会主导执行时间。请注意,无论使用的块大小如何,所有时间都在3毫秒左右

通过在循环中多次启动内核并平均执行时间,您可能会得到更精确的结果,但是对于这样一个小的内核调用,由于启动和块调度开销主导实际内核执行时间,这可能只会确认所有启动都在大约相同的时间内执行


为了查看不同块大小的使用所产生的统计显著性结果,您可能需要做一些(远)比1600万整数加法更重要的事情。

首先,正如我在评论中提到的,当在游戏卡上计时时,特别是对于这样短的测试,预期结果会出现波动。它们是动态计时的,并且时钟在每次运行时不会相同

根据我的经验,块大小/形状不太可能对这种简单实现的元素问题产生太大影响。只要您的区块x维度是32的倍数,并且您有足够大的区块来获得100%的占用率。在那之后,它只是将数据流输入和输出

但是,您可以做得比您的实现更好。现在已经很老了,但也有一些好处。从本质上讲,计算每个线程的多个元素将为您提供更好的性能


另一个(小的)性能改进将来自对内存事务的矢量化。如果在Int4而不是Int上操作,硬件可以发出128字节的加载/存储指令,而不是32字节的加载/存储指令。由于要处理的内存指令减少了4倍,因此这会稍微提高效率。

您使用的是动态计时的游戏卡。这使得计时结果非常不稳定。我认为内核启动开销是一个因素,通常是10个数量级。
#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;
}