Cuda 阵列点火定时
我试图评估使用ArrayFire的简单GPU元素矩阵运算的性能 特别是考虑到Cuda 阵列点火定时,cuda,parallel-processing,gpgpu,nvidia,arrayfire,Cuda,Parallel Processing,Gpgpu,Nvidia,Arrayfire,我试图评估使用ArrayFire的简单GPU元素矩阵运算的性能 特别是考虑到 int N1 = something; int N2 = something; array A_D = constant(1.,N1*N2,1,f64); array B_D = constant(1.,N1*N2,1,f64); array C_D = constant(1.,N1*N2,1,f64); array D_D = constant(1.,N1*N2,1,f64); 我想执行以下指令的计时 D_D =
int N1 = something;
int N2 = something;
array A_D = constant(1.,N1*N2,1,f64);
array B_D = constant(1.,N1*N2,1,f64);
array C_D = constant(1.,N1*N2,1,f64);
array D_D = constant(1.,N1*N2,1,f64);
我想执行以下指令的计时
D_D = A_D + B_D + C_D + 3.;
我使用两种方法。第一个是
timer time_last;
time_last = timer::start();
D_D = A_D + B_D + C_D + 3.;
double elapsed = timer::stop(time_last);
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed);
第二个是定义以下函数
void timing_test()
{
int N1 = something;
int N2 = something;
array A_D = constant(1.,N1*N2,1,f64);
array B_D = constant(1.,N1*N2,1,f64);
array C_D = constant(1.,N1*N2,1,f64);
array D_D = constant(1.,N1*N2,1,f64);
D_D = A_D + B_D + C_D + 3.;
}
然后打电话
printf("elapsed time using timeit %g ms \n", 1000.*timeit(timing_test));
我取得了以下成果:
(N1,N2)=(256256)
第一种方法=0.0456ms
第二种方法=0.264ms
(N1,N2)=(512512)
第一种方法=0.0451ms
第二种方法=0.264ms
(N1,N2)=(10241024)
第一次进近=0.0457ms
第二次进近=0.263ms
(N1,N2)=(20482048)
第一次进近=0.127ms
第二次进近=0.265ms
我还使用了以下“手工编码”版本的表达式,根据
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
eval_matrix_wrap_handcoded(A_D,B_D,C_D,D_D,N1*N2);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
template <class T1, class T2, class T3, class T4>
__global__ inline void evaluation_matrix_handcoded(T1 *A_D, T2 *B_D, T3 *C_D, T4 *D_D, int NumElements)
{
const int i = blockDim.x * blockIdx.x + threadIdx.x;
if(i < NumElements) D_D[i]=A_D[i]+B_D[i]+C_D[i]+3.;
}
__host__ void eval_matrix_wrap_handcoded(double *A_D, double *B_D, double *C_D, double *D_D, int NumElements)
{
dim3 dimGrid(iDivUp(NumElements,dimBlock.x));
evaluation_matrix_handcoded<<<dimGrid,dimBlock>>>(A_D,B_D,C_D,D_D,NumElements);
}
有人能帮我解释一下上述结果吗?谢谢
根据PAVAN的回答进行编辑
第一种方法修改为
timer time_last;
time_last = timer::start();
D_D = A_D + B_D + C_D + 3.;
D_D.eval();
af::sync();
double elapsed = timer::stop(time_last);
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed);
void timing_test()
{
int N1 = something;
int N2 = something;
array A_D = constant(1.,N1*N2,1,f64);
array B_D = constant(1.,N1*N2,1,f64);
array C_D = constant(1.,N1*N2,1,f64);
array D_D = constant(1.,N1*N2,1,f64);
D_D = A_D + B_D + C_D + 3.;
D_D.eval();
}
第二种方法修改为
timer time_last;
time_last = timer::start();
D_D = A_D + B_D + C_D + 3.;
D_D.eval();
af::sync();
double elapsed = timer::stop(time_last);
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed);
void timing_test()
{
int N1 = something;
int N2 = something;
array A_D = constant(1.,N1*N2,1,f64);
array B_D = constant(1.,N1*N2,1,f64);
array C_D = constant(1.,N1*N2,1,f64);
array D_D = constant(1.,N1*N2,1,f64);
D_D = A_D + B_D + C_D + 3.;
D_D.eval();
}
然而,现在时机已经成熟
`(N1,N2)=(256,256)` first approach = `14.7ms` second approach = `2.04ms`
`(N1,N2)=(512,512)` first approach = `14.3ms` second approach = `2.04ms`
`(N1,N2)=(1024,1024)` first approach = `14.09ms` second approach = `2.04ms`
`(N1,N2)=(2048,2048)` first approach = `16.47ms` second approach = `2.04ms`
我仍然有不同的时间和独立的向量大小
如果我将第一个方法修改为
D_D = A_D + B_D + C_D + 3.;
D_D.eval();
timer time_last;
time_last = timer::start();
D_D = A_D + B_D + C_D + 3.;
D_D.eval();
af::sync();
double elapsed = timer::stop(time_last);
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed);
也就是说,我“增加”GPU预热阶段,对于第一种方法
`(N1,N2)=(256,256)` `0.19ms`
`(N1,N2)=(512,512)` `0.42ms`
`(N1,N2)=(1024,1024)` `1.18ms`
`(N1,N2)=(2048,2048)` `4.2ms`
这对我来说更合理,因为计时取决于数据大小,更接近手工编码
第二次编辑
总结一下:我已经解释了答案和评论,对于第一种方法,我正在使用
D_D = A_D + B_D + C_D + 3.;
D_D.eval();
timer time_last;
af::sync();
time_last = timer::start();
D_D = A_D + B_D + C_D + 3.;
D_D.eval();
af::sync();
double elapsed = timer::stop(time_last);
printf("elapsed time using start and stop = %g ms \n",1000.*elapsed);
我获得了以下(新)结果:
ArrayFire使用即时编译器进行元素操作(包括算术、逻辑、三角测量和其他数学操作) 这意味着
D_D = A_D + B_D + C_D + 3.;
作为表达式存储,直到用户或其他非jit函数请求D_D的值
如果使用af::eval()
函数或eval()
方法,则可以强制计算这些表达式
因此,对于您的特定问题,请对这两种方法使用
D_D.eval()
。对于第一种方法,您还需要执行af::sync()
timeit()
不需要显式同步。非常感谢您的回答。我已经采纳了你的建议,但似乎我还有问题。我进一步编辑了我的帖子,以展示最新的结果。似乎,如果我“增加”GPU的预热量,那么我会得到更合理的结果。这些是确定的时间。我遗漏了什么吗?@Jackolanten我应该提到的。第一种方法需要预热一次(即时编译器需要生成所需的内核)。在第一个timer::start()
之前添加一个af::sync()
。谢谢。我已经用新的结果更新了我的帖子,包括您推荐的附加af::sync()
。它们看起来很合理,因为时间随着向量的大小而增加。但是,我现在是否应该对timeit
案例(第二种方法)执行任何操作以获得类似的结果?我仍然得到一个恒定的2ms
。提前感谢您的进一步帮助。