Cuda 阵列点火定时

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 =

我试图评估使用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)=(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
。提前感谢您的进一步帮助。