二维阵列的cudaMalloc与cudaMalloc3D性能
我想知道在为2D阵列分配、复制和访问内存时使用cudaMalloc或cudaMalloc3D对性能的影响。我有一些代码,我试图测试运行时,其中一个使用cudamaloc,另一个使用cudamaloc3d。我已经包括了下面的代码。如果您能解释一下这两种api对性能的影响,我们将不胜感激 Cudamaloc代码:二维阵列的cudaMalloc与cudaMalloc3D性能,c,cuda,C,Cuda,我想知道在为2D阵列分配、复制和访问内存时使用cudaMalloc或cudaMalloc3D对性能的影响。我有一些代码,我试图测试运行时,其中一个使用cudamaloc,另一个使用cudamaloc3d。我已经包括了下面的代码。如果您能解释一下这两种api对性能的影响,我们将不胜感激 Cudamaloc代码: #include <stdio.h> #include <stdlib.h> #include <math.h> #define PI 3.14159
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14159265
#define NX 8192 /* includes boundary points on both end */
#define NY 4096 /* includes boundary points on both end */
#define N_THREADS_X 16
#define N_THREADS_Y 16
#define N_BLOCKS_X NX/N_THREADS_X
#define N_BLOCKS_Y NY/N_THREADS_Y
#define LX 4.0 /* length of the domain in x-direction */
#define LY 2.0 /* length of the domain in x-direction */
#define dx (REAL) ( LX/( (REAL) (NX) ) )
#define cSqrd 5.0
#define dt (REAL) ( 0.4 * dx / sqrt(cSqrd) )
#define FACTOR ( cSqrd * (dt*dt)/(dx*dx) )
#define IC (i + j*NX) /* (i,j) */
#define IM1 (i + j*NX - 1) /* (i-1,j) */
#define IP1 (i + j*NX + 1) /* (i+1,j) */
#define JM1 (i + (j-1)*NX) /* (i,j-1) */
#define JP1 (i + (j+1)*NX) /* (i,j+1) */
#define cudaCheckError() {\
cudaError_t e = cudaGetLastError() ; \
if( e != cudaSuccess ) {\
printf("\nCuda Failure %s:%d: %s\n",__FILE__,__LINE__,cudaGetErrorString(e));\
exit(EXIT_FAILURE);\
}\
}
typedef double REAL;
typedef int INT;
__global__ void solveWaveGPU ( REAL *uold, REAL *u, REAL *unew )
{
INT i,j;
i = blockIdx.x*blockDim.x + threadIdx.x;
j = blockIdx.y*blockDim.y + threadIdx.y;
if (i>0 && i < (NX-1) && j>0 && j < (NY-1) ) {
unew[IC] = 2.0*u[IC] - uold[IC] + FACTOR*( u[IP1] + u[IM1] + u[JP1] + u[JM1] - 4.0*u[IC] );
}
}
void initWave ( REAL *unew, REAL *u, REAL *uold, REAL *x, REAL *y )
{
INT i,j;
for (j=1; j<NY-1; j++) {
for (i=1; i<NX-1; i++) {
u[IC] = 0.1 * (4.0*x[IC]-x[IC]*x[IC]) * ( 2.0*y[IC] - y[IC]*y[IC] );
}
}
for (j=1; j<NY-1; j++) {
for (i=1; i<NX-1; i++) {
uold[IC] = u[IC] + 0.5*FACTOR*( u[IP1] + u[IM1] + u[JP1] + u[JM1] - 4.0*u[IC] );
}
}
}
void meshGrid ( REAL *x, REAL *y )
{
INT i,j;
REAL a;
for (j=0; j<NY; j++) {
a = dx * ( (REAL) j );
for (i=0; i<NX; i++) {
x[IC] = dx * ( (REAL) i );
y[IC] = a;
}
}
}
INT main(INT argc, char *argv[])
{
INT nTimeSteps = 100;
REAL *unew, *u, *uold, *uFinal, *x, *y; //pointers for the host side
REAL *d_unew, *d_u, *d_uold, *tmp; //pointers for the device
// variable declaration for timing
cudaEvent_t timeStart, timeStop;
cudaEventCreate(&timeStart);
cudaEventCreate(&timeStop);
float elapsedTime_gpu;
unew = (REAL *)calloc(NX*NY,sizeof(REAL));
u = (REAL *)calloc(NX*NY,sizeof(REAL));
uold = (REAL *)calloc(NX*NY,sizeof(REAL));
uFinal = (REAL *)calloc(NX*NY,sizeof(REAL));
x = (REAL *)calloc(NX*NY,sizeof(REAL));
y = (REAL *)calloc(NX*NY,sizeof(REAL));
// create device copies of the variables
cudaMalloc( (void**) &d_unew, NX*NY*sizeof(REAL) ); cudaCheckError();
cudaMalloc( (void**) &d_u, NX*NY*sizeof(REAL) ); cudaCheckError();
cudaMalloc( (void**) &d_uold, NX*NY*sizeof(REAL) ); cudaCheckError();
meshGrid( x, y );
initWave( unew, u, uold, x, y );
// start timing the GPU
cudaMemcpy( d_u, u, NX*NY*sizeof(REAL), cudaMemcpyHostToDevice ); cudaCheckError();
cudaMemcpy( d_uold, uold, NX*NY*sizeof(REAL), cudaMemcpyHostToDevice ); cudaCheckError();
cudaMemcpy( d_unew, unew, NX*NY*sizeof(REAL), cudaMemcpyHostToDevice ); cudaCheckError();
// set up the GPU grid/block model
dim3 dimGrid ( N_BLOCKS_X , N_BLOCKS_Y );
dim3 dimBlock ( N_THREADS_X, N_THREADS_Y );
// launch the GPU kernel
cudaEventRecord(timeStart, 0);
for (INT n=1; n<nTimeSteps+1; n++) {
solveWaveGPU <<<dimGrid,dimBlock>>>(d_uold, d_u, d_unew);
cudaDeviceSynchronize();
cudaCheckError();
tmp = d_uold;
d_uold = d_u;
d_u = d_unew;
d_unew = tmp;
}
cudaEventRecord(timeStop, 0);
cudaEventSynchronize(timeStop);
cudaEventElapsedTime(&elapsedTime_gpu, timeStart, timeStop);
cudaMemcpy( uFinal, d_u, NX*NY*sizeof(REAL), cudaMemcpyDeviceToHost ); cudaCheckError();
printf("elapsedTime on the GPU= %f s.\n", elapsedTime_gpu/1000.0);
free(unew); free(u); free(uold);
cudaFree(d_unew); cudaFree(d_u); cudaFree(d_uold);
free(uFinal); free(x); free(y);
cudaEventDestroy(timeStart);
cudaEventDestroy(timeStop);
return (0);
}
机器规格:
GNU/Linux x86_64
NVIDIA GeForce GTX Titan CC: 3.5
CUDA ver 7.0
您观察到的性能差异主要是由于倾斜内存索引方案中增加的指令开销。由于您的数组大小在主方向上是2的大幂,因此使用
cudamaloc3d
分配的倾斜数组很可能与使用cudamaloc3d
分配的原始数组大小相同。如果您改变问题的大小,您可能会发现两个版本之间的性能差异会发生变化
(请注意CUDA 7中有关编译器回归的评论。如果您重构代码以将傅立叶数作为内核参数传递,您可能会得到比因倾斜内存而产生的任何差异都大得多的性能变化)。我建议您将代码发布在问题中,而不是外部github repo中。如果从代码中看不清楚,您还应该定义如何进行计时测量,并给出准确的计时结果,以及机器规格:CPU、GPU、OS、CUDA版本。在配备sm_30的linux机器上,我测量
cudamaloc3d
版本比cudamaloc
版本慢约10%(平均0.945秒,而平均5次程序运行时为0.812秒)这是一个有趣的例子,因为它揭示了CUDA 7和CUDA 6.x之间巨大的性能回归。CUDA 7编译器做了一件非常愚蠢的事情,直接将计算dt
而不是像CUDA 6.x编译器那样在编译时进行计算。这是这些内核上的编译器之间性能损失的2个因素。但我想这两个内核之间的速度差异只是由于在倾斜内存版本中增加了数组索引计算的开销eve速度差异是由于增加了开销。我做了分析,并确认内存加载效率或内存存储效率没有变化,但转储sass为更快的内核生成了约250条指令,为较慢的内核生成了约300条指令。感谢CUDA 6.5与CUDA 7上的指针,我一直在测试仅适用于CUDA 7。我将查看CUDA 6.5,如果我能确认您的观察,我将提交一个错误。谢谢。
cudaMalloc3D: 1.192510 s
cudaMalloc: 0.960322 s
GNU/Linux x86_64
NVIDIA GeForce GTX Titan CC: 3.5
CUDA ver 7.0