MATLAB速度优化

时间:2013-10-04 10:09:52

标签: performance matlab parallel-processing

有人可以帮忙吗?我是一位相当有经验的Matlab用户,但我在加速下面的代码时遇到了问题。

我使用12个核心在所有三个循环中运行的最快时间是〜200s。实际功能将被调用~720次,并且以此速率执行需要40多个小时。根据Matlab分析器,大部分cpu时间都花在指数函数调用中。我已经设法使用gpuArray大幅度提高了速度,然后在Quadro 4000显卡上运行exp调用,但这会阻止使用parfor循环,因为工作站只有一个显卡,可以消除任何收益。任何人都可以提供帮助,或者这段代码是否接近使用Matlab可以实现的最佳值?我用openMP编写了一个非常粗略的c ++实现,但收效甚微。

非常感谢提前

function SPEEDtest_CPU

% Variable setup:
% - For testing I'll use random variables. These will actually be fed into 
%   the function for the real version of this code.
sy    = 320;
sx    = 100;
sz    = 32;
A     = complex(rand(sy,sx,sz),rand(sy,sx,sz));
B     = complex(rand(sy,sx,sz),rand(sy,sx,sz));
C     = rand(sy,sx);
D     = rand(sy*sx,1);
F     = zeros(sy,sx,sz);
x     = rand(sy*sx,1);  
y     = rand(sy*sx,1);
x_ind = (1:sx) - (sx / 2) - 1;
y_ind = (1:sy) - (sy / 2) - 1;


% MAIN LOOPS 
%  - In the real code this set of three loops will be called ~720 times!
%  - Using 12 cores, the fastest I have managed is ~200 seconds for one
%    call of this function.
tic
for z = 1 : sz
    A_slice = A(:,:,z);
    A_slice = A_slice(:);
    parfor cx = 1 : sx       
        for cy = 1 : sy       
            E = ( x .* x_ind(cx) ) + ( y .* y_ind(cy) ) + ( C(cy,cx) .* D );                                                          

            F(cy,cx,z) = (B(cy,cx,z) .* exp(-1i .* E))' * A_slice; 
        end       
    end   
end
toc

end

8 个答案:

答案 0 :(得分:3)

要考虑的一些事情:

你考虑过使用单打吗?

你可以对cx,cy部分进行矢量化,以便它们代表数组运算吗?

考虑更改浮点舍入或信令模式。

答案 1 :(得分:2)

如果您的数据是真实的(不复杂),如您的示例所示,您可以节省更换时间

(B(cy,cx,z) .* exp(-1i .* E))'

通过

(B(cy,cx,z) .* (cos(E)+1i*sin(E))).'

具体来说,在我的计算机(cos(x)+1i*sin(x)).'上, 19%的时间少于exp(-1i .* x)'


如果AB很复杂:E仍然是真实的,那么您可以在循环外预先计算Bconj = conj(B)(这需要大约10毫秒的数据大小,并且它只完成了一次)然后替换

(B(cy,cx,z) .* exp(-1i .* E))'

通过

(Bconj(cy,cx,z) .* (cos(E)+1i*sin(E))).'

获得类似的收益。

答案 2 :(得分:1)

加速MATLAB代码有两种主要方式; 预分配矢量化

您已预先分配好,但没有矢量化。为了最好地学习如何做到这一点,你需要很好地掌握线性代数,并使用repmat将向量扩展到多个维度。

矢量化可以带来多个数量级的加速,并且可以最佳地使用核心(如果标志已经启动)。

你在计算的数学表达式是什么,我可以伸出援助之手?

答案 3 :(得分:1)

您可以将x .* x_ind(cx)移出最里面的循环。我没有方便的GPU来测试时序,但是您可以将代码分成三个部分以允许您使用GPU和parfor

for z = 1 : sz
    E = zeros(sy*sx,sx,sy);
    A_slice = A(:,:,z);
    A_slice = A_slice(:);
    parfor cx = 1 : sx
        temp = ( x .* x_ind(cx) );       
        for cy = 1 : sy       
            E(:, cx, cy) = temp + ( y .* y_ind(cy) ) + ( C(cy,cx) .* D );                                                          
        end
    end
    temp = zeros(zeros(sy*sx,sx,sy));
    for cx = 1 : sx
        for cy = 1 : sy       
             % Ideally use your GPU magic here
             temp(:, cx, cy) = exp(-1i .* E(:, cx, cy)));
        end
    end
    parfor cx = 1 : sx
        for cy = 1 : sy       
            F(cy,cx,z) = (B(cy,cx,z) .* temp(:, cx, cy)' * A_slice; 
        end       
    end   
end

答案 4 :(得分:0)

为了允许正确的并行化,你需要确保循环是完全独立的,因此检查在每次运行中是否没有分配给E有帮助。

此外,尽量尝试矢量化,一个简单的例子可能是:y.*y_ind(cy)

如果您只是一次为所有值创建正确的索引,则可以将其从最低循环中取出。

答案 5 :(得分:0)

不确定它是否对速度有多大帮助 - 但由于E基本上是一个总和,你可以使用exp (i cx(A+1)x) = exp(i cx(A) x) * exp(i x)exp(i x)可以预先计算。

这样你就不必在每次迭代时评估exp - 但只需要加倍,这应该更快。

答案 6 :(得分:0)

除了其他人给出的其他好的建议之外,A_slice的乘法与cx,cy循环无关,可以在它们之外取得,一旦两个循环都有F完了。

同样,B*exp(...)的共轭也可以在cx,cy循环之外进行整数,然后乘以A_slice

答案 7 :(得分:0)

该行:(x。* x_ind(cx))+(y。* y_ind(cy))+(C(cy,cx)。* D);

是某种类型的卷积,不是吗?循环卷积在频域中快得多,并且使用FTT优化了到/来自频域的转换。