(已经正确并详细回答,请见下方)
我开始使用Matlab和GPU(Nvidia GTX660)进行实验。
现在,我编写了这个简单的蒙特卡罗算法来计算圆周率。下面是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上执行起来所需的时间非常短,它会将100,000个点“投”到单位圆中:
>> 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! ;)
pig = nnz(sum(X.^2,2)<=1) * 4 / size(X,1)
其中X=rand(N,2)
或类似于 GPU 上的数组。 - Amro