(正确且有指导性地回答,见下文)
我开始用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-ized版本的算法会发生什么:
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.
我不明白为什么。我使用parfor循环和1024个工作程序,因为使用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版本(/ parfor),因为它的执行需要几个小时,n = 10000000。
伟大的比奇伊! ;)
答案 0 :(得分:3)
我想问题出在parfor
!
parfor
应该在MATLAB工作者上运行,这是你的主机而不是GPU!
我想实际发生的是你在你的主机上启动了1024个线程(而不是你的GPU),并且每个线程都试图调用GPU。这导致您的代码花费了大量时间。
尝试重新编写代码以使用矩阵和数组操作,而不是for-loops!这将显示一些加速。此外,请记住,您应该在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 :(得分:1)
许多原因如:
rand
可能不是并行if
条件会导致分歧很难分析Matlab + CUDA代码。您应该尝试使用本机C ++ / CUDA并使用并行Nsight来找到瓶颈。
答案 2 :(得分:1)
正如Bichoy所说,CUDA代码应该始终进行矢量化。在MATLAB中,除非您正在编写CUDA Kernal,否则您获得的唯一大型加速是在具有数千个(慢)内核的GPU上调用矢量化操作。如果你没有大型矢量和矢量化代码,它就不会有帮助。
还没有提到的另一件事是,对于像GPU这样的高度并行架构,你想使用不同的随机数生成算法而不是&#34;标准&#34;那些。所以要添加Bichoy的答案,添加参数&#39; Threefry4x64&#39; (64位)或&#39; Philox4x32-10&#39; (32位且速度更快!超快!)可以导致CUDA代码的大幅加速。 MATLAB在此解释了这一点:http://www.mathworks.com/help/distcomp/examples/generating-random-numbers-on-a-gpu.html