Matlab CUDA基础实验



(正确且有指导意义的回答,见下文)

我开始用matlab和gpu (nvidia gtx660)做实验。

现在,我写了这个简单的蒙特卡罗算法来计算PI。CPU版本如下:

function pig = mc1vecnocuda(n)
countr=0;
A=rand(n,2);
 for i=1:n
   if norm(A(i,:))<1
    countr=countr+1;
   end
 end
pig=(countr/n)*4;
end

这需要非常少的时间在CPU上执行,100000点被"抛出"到单位圆中:

   >> tic; mc1vecnocuda(100000);toc;
      Elapsed time is 0.092473 seconds.

让我们看看gpu化版本的算法:

   function pig = mc1veccuda(n)
   countr=0;
   gpucountr=gpuArray(countr);
   A=gpuArray.rand(n,2);
   parfor (i=1:n,1024)
    if norm(A(i,:))<1
        gpucountr=gpucountr+1;
    end
   end
   pig=(gpucountr/n)*4;
   end

现在,这需要很长时间来执行:

>> tic; mc1veccuda(100000);toc;
Elapsed time is 21.137954 seconds.

我不明白为什么。我使用了有1024个工作线程的parfor循环,因为用gpuDevice查询我的nvidia卡,1024是gtx660允许的最大并发线程数。

有人能帮帮我吗?谢谢。

编辑:这是避免IF的更新版本:

function pig = mc2veccuda(n)
countr=0;
gpucountr=gpuArray(countr);
A=gpuArray.rand(n,2);
parfor (i=1:n,1024)
    gpucountr = gpucountr+nnz(norm(A(i,:))<1);
end
pig=(gpucountr/n)*4;
end

这是按照Bichoy的指导原则编写的代码代码实现结果):

function pig = mc3veccuda(n)
countr=0;
gpucountr=gpuArray(countr);
A=gpuArray.rand(n,2);
Asq = A.^2;
Asqsum_big_column = Asq(:,1)+Asq(:,2);
Anorms=Asqsum_big_column.^(1/2);
gpucountr=gpucountr+nnz(Anorms<1);
pig=(gpucountr/n)*4;
end

请注意n= 1000万时的执行时间:

>> tic; mc3veccuda(10000000); toc;
Elapsed time is 0.131348 seconds.
>> tic; mc1vecnocuda(10000000); toc;
Elapsed time is 8.108907 seconds.

我没有测试我的原始cuda版本(for/parfor),因为当n=10000000时,它的执行需要几个小时。

大Bichoy !div;)

我猜问题出在parfor !

parfor应该在MATLAB工作器上运行,那是您的主机而不是GPU!我猜实际发生的情况是,您在主机上启动了1024个线程(而不是在GPU上),并且每个线程都试图调用GPU。这将导致代码花费大量时间。

试着重写你的代码,使用矩阵和数组操作,而不是for循环!这将显示一些加速。另外,请记住,你应该在GPU上做更多的计算,否则,内存传输将会支配你的代码。 <标题>代码:

这是经过几个人的修改和建议后的最终代码:

function pig = mc2veccuda(n)
  A=gpuArray.rand(n,2); % An nx2 random matrix
  Asq = A.^2; % Get the square value of each element
  Anormsq = Asq(:,1)+Asq(:,2); % Get the norm squared of each point
  gpucountr = nnz(Anorm<1); % Check the number of elements < 1
  pig=(gpucountr/n)*4;

原因很多,比如:

  1. 主机之间的数据移动&设备
  2. 每个循环内的计算量非常小
  3. 在GPU上调用rand可能不是并行的
  4. 环内if条件可引起发散
  5. 累积到一个公共变量可以串行运行,开销

很难配置Matlab+CUDA代码。你可能应该尝试在本地c++/CUDA中使用并行的Nsight来找到瓶颈

正如Bichoy所说,CUDA代码应该始终进行矢量化。在MATLAB中,除非你正在编写CUDA内核,否则你得到的唯一大的加速是在GPU上调用矢量化操作,而GPU有数千个(缓慢的)内核。如果你没有大的矢量和向量化代码,这是没有用的。


另一件没有提到的事情是,对于像gpu这样的高度并行架构,你想使用不同于"标准"算法的随机数生成算法。所以要添加到Bichoy的答案,添加参数'Threefry4x64'(64位)或'Philox4x32-10'(32位和更快!超级快!)可以大大提高CUDA代码的速度。MATLAB在此解释:http://www.mathworks.com/help/distcomp/examples/generating-random-numbers-on-a-gpu.html

最新更新