有人可以帮忙吗?我是一位相当有经验的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
答案 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)'
。
如果A
和B
很复杂: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优化了到/来自频域的转换。