为什么MATLAB gpuarray在仅仅添加两个矩阵时要慢得多?

为什么MATLAB gpuarray在仅仅添加两个矩阵时要慢得多?,matlab,gpgpu,gpu,Matlab,Gpgpu,Gpu,我最近使用MatlabCuda库在gpu上进行了一些绝对简单的矩阵计算。但是性能结果非常奇怪。 有谁能帮助我了解到底发生了什么,以及我如何解决这个问题。提前谢谢。 请注意,以下代码在geforce GTX TITAN black gpu上运行 假设a0,a1,…a6为1000*1000 gpa射线,U=0.5,V=0.0 titan = gpuDevice(); tic(); for i=1:10000 a6(1,1)=(0.5.*(a5(1,1)-a0(1,1)))-(a1(1,1)+a2(

我最近使用MatlabCuda库在gpu上进行了一些绝对简单的矩阵计算。但是性能结果非常奇怪。 有谁能帮助我了解到底发生了什么,以及我如何解决这个问题。提前谢谢。 请注意,以下代码在geforce GTX TITAN black gpu上运行

假设a0,a1,…a6为1000*1000 gpa射线,U=0.5,V=0.0

titan = gpuDevice();
tic();

for i=1:10000
a6(1,1)=(0.5.*(a5(1,1)-a0(1,1)))-(a1(1,1)+a2(1,1)+a3(1,1))-(a5(1,1).*U./3.0)-(a5(1,1).*V./2.0)+(0.25.*a5(1,1).*a4(1,1));  
end

wait(titan);
time = toc()
时间结果=17.98秒

现在重新定义在cpu上使用的a0、a1、…a6、U和V,并计算所需时间:

tic();

for i=1:10000
a6(1,1)=(0.5.*(a5(1,1)-a0(1,1)))-(a1(1,1)+a2(1,1)+a3(1,1))-(a5(1,1).*U./3.0)-(a5(1,1).*V./2.0)+(0.25.*a5(1,1).*a4(1,1));  
end

time= toc()  
时间结果=0.0098秒

因此,cpu速度提高1800倍以上

然后我决定对整个矩阵而不是特定元素进行前面的计算,结果如下:

在gpu上运行的结果:

titan = gpuDevice();
tic();
for i=1:10000
a6=(0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);  
end
wait(titan);
time = toc()   
时间的结果=6.32秒 这意味着对整个矩阵的运算要比对特定元素的运算快得多

在CPU上运行的结果:

tic();
for i=1:10000
a6=(0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);  
end

time= toc()  
时间的结果=35.2秒

以下是最令人惊讶的结果: 假设a0、a1、…a6以及U和V仅为1*1 gpuarray,并运行以下程序:

titan = gpuDevice();
tic();
for i=1:10000
a6=(0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);  
end
wait(titan);
time = toc()  
时间结果=7.8秒

它甚至比对应的1000*1000箱还要慢

不幸的是,这条线 a6(1,1)=(0.5.*(a5(1,1)-a0(1,1))-(a1(1,1)+a2(1,1)+a3(1,1))-(a5(1,1)。*U./3.0)-(a5(1,1)。*V./2.0)+(0.25.*a5(1,1)。*a4(1,1)); 是大约100条线路中的一条,全部在一个for循环中,这条线路证明自己是一个真正的瓶颈,占用了大约50%的计算时间! 有人能帮我吗?请注意,在cpu上传输这部分计算不是一种选择,因为瓶颈线位于for循环中,在每次迭代中将a1,…a6发送到cpu并将结果调用到gpu要耗时得多。 非常感谢您的建议。

ehsan

泰坦是强大的

我希望以下内容可能会有所帮助

1> GPU有许多(从数百到数千)低频内核,这意味着它们必须执行相同的指令。所以,他们非常擅长指导。 如果您只计算矩阵的一个元素(第一个示例和最后一个示例),那么GPU肯定不擅长这一点

2> 对于第二个测试,请将索引i包含到表达式中,以消除编译器中的优化。或者,您可以尝试将10000更改为50000,以查看是否存在差异

for i=1:10000
a6=i*(0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);  
end    
3> CPU有自己的(VPU),也是针对SIMD的。唯一的问题是,它非常小,从64位到256位。因此,如果矩阵很小,CPU就比GPU好得多。因此,要看到GPU的性能优势,您可以尝试更大的尺寸,例如5000x5000


请让我知道,如果你有任何进一步的结果在这方面

我认为您的第二个GPU结果(即矢量化GPU调用)是最相关的-GPU在以矢量化方式对大量数据进行操作时效率最高。在您的例子中,通过将表达式转换为
arrayfun
调用,您可能会获得更好的性能
arrayfun
允许MATLAB将整个表达式转换为GPU上的单个操作,从而充分利用设备的(巨大)可用内存带宽

关于计算a6(1,1)的问题-也许最好先计算整个数组(即不为右侧表达式编制索引),然后再编制索引。差不多

tmp = (0.5.*(a5-a0))-(a1+a2+a3)-(a5.*U./3.0)-(a5.*V./2.0)+(0.25.*a5.*a4);
a6(1,1) = tmp(1,1);

我刚刚用我的Titan black和matlab 2011b验证了这一点-时间当然不同,但趋势是相同的。一些想法:参考5个代码块,2比1快得多并不奇怪,因为CPU比单个GPU多进程快得多,CPU将缓存所有值。3和4一致-GPU更快。1对3:也许在gpu上建立索引的成本很高?我在www上找到一些资料表明GPU不支持索引,但没有关于Matlab版本的详细信息。不知道五点。一般说明:使用*和/代替。*和/对于标量,速度更快。还有:您使用的是单精度gpuarray吗?(变化不大,但我的matlab版本比较旧)。另外,对我来说,在cpu或gpu上定义U和V会产生一个微小但不可忽略的差异。此外,测量10.000x for循环可能是不公平的,因为可能每次迭代都会创建一个新的cuda内核,或者其他什么。这里的GPU测试CPU是100%。还有一个问题,你是如何发现这句话占50%的?这可靠吗?例如,将单行计算时间与循环的整个时间进行比较是不公平的。您可以编写自己的内核,我使用双精度,但我认为单精度可以带来真正的提升。另一方面,我说的50%是指for循环中大约有100行代码,但是分析显示大约50%的执行时间用于这一特殊行。matlab中是否有用于此的gpu分析器?或者你用什么?我的意思是,您不能通过从代码中删除这个或那个部分,然后对其进行基准测试来可靠地评测,因为将创建一个新的、以不同方式优化的内核。如果您尝试以这种方式分析,那么认为这条线确实是瓶颈的结论可能是错误的。Thomas,我使用了原始的MATLAB profiler,但为了提高准确性,我在某些LinesDric之后添加了一个“wait”命令,我将测试“arrayfun”和“not indexing”两个建议,并让您知道结果。还有什么意见为什么第三个测试(1*1矩阵)比第二个测试(1000*1000矩阵)慢?我必须承认,我不完全确定为什么标量情况会变慢——这可能与启动的内核的精确形式以及它们能够重叠的程度有关。Edric,我测试了arrayfun的建议。幸运的是,它带来了更好的性能。第一次测试得5.43分