我试图用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 = 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)=(256,256)第一方法= 0.0456ms第二方法= 0.264ms
(N1,N2)=(512,512)第一方法= 0.0451ms第二方法= 0.264ms
(N1,N2)=(1024,1024)第一方法= 0.0457ms第二方法= 0.263ms
(N1,N2)=(2048,2048)第一方法= 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);
}获取以下信息
(N1,N2)=(256,256) 0.0897ms
(N1,N2)=(512,512) 0.339ms
(N1,N2)=(1024,1024) 1.3ms
(N1,N2)=(2048,2048) 5.37ms
奇怪的是
(N1,N2)=(2048,2048)时,这种开销会发生变化。请注意,在任何操作之前,我将使用以下代码对GPU进行热身
array test1(1,5);
test1(0,0)=1;
test1(0,1)=2;
test1(0,2)=3;
test1(0,3)=4;
test1(0,4)=5;有人能帮我解释一下上面的结果吗?谢谢。
编辑帕文的答案
第一种方法
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);我正在获得以下(新的)结果:
`(N1,N2)=(256,256)` `0.18ms`
`(N1,N2)=(512,512)` `0.30ms`
`(N1,N2)=(1024,1024)` `0.66ms`
`(N1,N2)=(2048,2048)` `2.18ms`发布于 2013-04-03 19:41:19
ArrayFire使用实时编译器对元素进行操作(这包括算术、逻辑、三角和其他数学操作)。
这意味着
D_D = A_D + B_D + C_D + 3.;作为表达式存储,直到用户或另一个非jit函数请求D_D的值为止。
如果使用af::eval()函数或eval()方法,则可以强制计算这些表达式。
因此,对于您的特殊问题,请对这两种方法使用D_D.eval()。您还需要对第一个方法执行af::sync()。timeit()不需要显式同步。
https://stackoverflow.com/questions/15790483
复制相似问题