(正确且有指导意义的回答,见下文)
我开始用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。这将导致代码花费大量时间。
这是经过几个人的修改和建议后的最终代码:
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;
标题>原因很多,比如:
- 主机之间的数据移动&设备
- 每个循环内的计算量非常小
- 在GPU上调用
rand
可能不是并行的 - 环内
if
条件可引起发散 - 累积到一个公共变量可以串行运行,开销
很难配置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