3

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

我开始用 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=10 百万的执行时间:

>> 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。

伟大的比乔伊!;)

4

3 回答 3

3

我想问题出在parfor!

parfor应该在 MATLAB worker 上运行,那是你的主机而不是 GPU!我猜实际发生的情况是您在主机(而不是 GPU)上启动了 1024 个线程,并且每个线程都在尝试调用 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;
于 2013-04-13T17:19:35.193 回答
1

很多原因比如:

  1. 主机和设备之间的数据移动
  2. 每个循环内的计算量非常小
  3. 对 GPU 的调用rand可能不是并行的
  4. if循环内的条件可能导致分歧
  5. 对公共变量的累加可以串行运行,有开销

很难分析 Matlab+CUDA 代码。您可能应该尝试使用本机 C++/CUDA 并使用并行 Nsight 来查找瓶颈。

于 2013-04-13T14:28:44.187 回答
1

正如 Bichoy 所说,CUDA 代码应该始终进行矢量化处理。在 MATLAB 中,除非您正在编写 CUDA 内核,否则您获得的唯一大幅加速是在具有数千个(慢速)内核的 GPU 上调用矢量化操作。如果您没有大型向量和向量化代码,那将无济于事。


另一件没有提到的事情是,对于像 GPU 这样的高度并行架构,您希望使用与“标准”算法不同的随机数生成算法。因此,添加到 Bichoy 的答案中,添加参数“Threefry4x64”(64 位)或“Philox4x32-10”(32 位,速度更快!超快!)可以导致 CUDA 代码的大幅加速。MATLAB 在这里解释了这一点:http: //www.mathworks.com/help/distcomp/examples/generating-random-numbers-on-a-gpu.html

于 2016-01-15T17:19:24.297 回答