作為較長演算法的一部分,填充列陣列b需要 80% 的總運行時間。在回圈中計算 S1 的那一行(見下面的代碼)for是瓶頸,因為由于嵌套回圈,它被呼叫的次數非常多。我嘗試過對回圈進行矢量化和/或使用sum內置函式,但我總是以更高的計算時間結束。
為簡單起見,我們可以看一次迭代,即n = 1和n 1 = 2。因此,在每次迭代中包含數量 R 的 3D 陣列只有兩個元素:R(:,:,1)和R(:,:,2)。

在哪里:

我在這里發布了一個您可以自己運行的最低作業示例:
tic
Nz = 3636 ; % Number of rows
Nx = 910 ; % Number of columns
R = rand(Nz, Nx, 2) ;
alpha = 3 ;
for ii = Nx - 1 : -1 : 2
b = zeros(Nz, 1) ;
for jj = 2 : Nz - 1
S = 0 ;
S1 = 0 ;
for mm = Nx - 1 : -1 : ii % Summation
S1 = S1 R(2, mm, 2) R(2, mm, 1) - (R(1, mm, 2) R(1, mm, 1)) ;
S = S (R(jj 1, mm, 2) R(jj 1, mm, 1)) - 2 * (R(jj, mm, 2) R(jj, mm, 1)) (R(jj - 1 , mm, 2) R(jj - 1, mm, 1)) ;
end
b(1) = 2 *(R(1, ii, 1) 2 * alpha * S1) ;
b(jj) = 2 *(R(jj, ii, 1) alpha * S) ;
end
end
toc
在我的筆記本電腦(i7-9850H,2.6 GHz,16 GB RAM)上,當前經過的時間約為 28-30 [s]。
I am aware that b is reset to zero at each ii iteration, but to reduce the sample to bare minimum I omitted a line just before the last end: q = D\b. It's a linear system solved column by column starting from the right-most column of the grid (most external loop). That's why at every ii (column) iteration I compute a new b and thus a new solution q.
How can I speed up this computation?
EDIT:
I tried to vectorize the sum, by eliminating the inner for loop and using the function sum, but the time increases dramatically.
tic
Nz = 3636 ; % Number of rows
Nx = 910 ; % Number of columns
R = rand(Nz, Nx, 2) ;
alpha = 3 ;
for ii = Nx - 1 : -1 : 2
b = zeros(Nz, 1) ;
for jj = 2 : Nz - 1
S1 = sum([R(2, Nx - 1 : -1 : ii, 2), R(2, Nx - 1 : -1 : ii, 1), - R(1, Nx - 1 : -1 : ii, 2), R(1,Nx - 1 : -1 : ii, 1)]) ;
S = sum([R(jj 1, Nx - 1 : -1 : ii, 2), R(jj 1, Nx - 1 : -1 : ii, 1), - 2 * R(jj, Nx - 1 : -1 : ii, 2), 2 * R(jj, Nx - 1 : -1 : ii, 1), R(jj - 1 , Nx - 1 : -1 : ii, 2), R(jj - 1, Nx - 1 : -1 : ii, 1)]) ;
b(1) = 2 *(R(1, ii, 1) 2 * alpha * S1) ;
b(jj) = 2 *(R(jj, ii, 1) alpha * S) ;
end
end
toc
uj5u.com熱心網友回復:
我只是在使用您優化的代碼。主要是因為您的優化代碼與未優化的代碼不一樣,所以有更多沒有意義的事情(b在所有ii回圈中都被替換,所以ii是多余的)。因此,我為您提供了一些總體上改進代碼的技巧,但無法解決問題。
尖端:
使您的代碼易于閱讀。
R(2, Nx - 1 : -1 : ii, 2), R(2, Nx - 1 : -1 : ii, 1)只是R(2, Nx - 1 : -1 : ii, 2:1),事實上你正在添加它們R(2, Nx - 1 : -1 : ii, :)。如果按預期(即按順序)對陣列進行索引會快得多。您正在添加陣列元素,但反向讀取它們。只需正常閱讀它們,即
Nx-1:-1:ii只是ii:Nx-1. 獎勵它更具可讀性。事實上,上面的行并沒有突然R(2, ii:Nx-1, :)變得更具可讀性。如果可以避免,請不要創建臨時陣列。特別是如果最后,這些將是
R! 你只是索引它,你不關心排序,因為你會把它加在一起。S可以只是:S = sum(R(jj-1:jj 1, ii : Nx-1, :).*cat(3,[1;2;1],[1; -2; 1]),'all') ; % R(jj-1:jj 1, ii : Nx-1, :) % grabs the needed values % cat(3,[1;2;1],[1; -2; 1]) % small trick to multiply the arrays (implicit broadcast) % sum( ,'all') % add them regarthless of shape.
這已經大大加快了代碼速度。
4-不要過度計算。S1不依賴于jj,事實上,它對每個都是一樣的ii。那么為什么要計算它的jj時間ii呢?
因此,您可以將優化后的代碼更改為以下代碼,以獲得 x1.7 加速:
tic
Nz = 1000 ; % Number of rows
Nx = 200 ; % Number of columns
R = rand(Nz, Nx, 2) ;
alpha = 3 ;
for ii = Nx - 1 : -1 : 2
b1 = zeros(Nz, 1) ;
% we can optimize this one too, but I don't think its the main culprit
% here.
S1 = sum([R(2, ii : Nx-1, 2), R(2, ii : Nx-1, 1),...
- R(1, ii : Nx-1, 2), R(1,ii : Nx-1, 1)]) ;
b1(1) = 2 *(R(1, ii, 1) 2 * alpha * S1) ;
for jj = 2 : Nz - 1
S = sum(R(jj-1:jj 1, ii : Nx-1, :).*cat(3,[1;2;1],[1; -2; 1]),'all') ;
b1(jj) = 2 *(R(jj, ii, 1) alpha * S) ;
end
end
toc
uj5u.com熱心網友回復:
在我的筆記本電腦(MATLAB R2020a、i7-5500U、2.4 GHz、8 GB RAM)上,您的原始代碼運行時間約為 45 秒。
Elapsed time is 45.239229 seconds.
您使用 sum 的嘗試大約需要 191 秒。
Elapsed time is 191.424418 seconds.
@Ander Biguri 建議運行時間約為 174 秒,速度更快,但不如使用for回圈快。
Elapsed time is 174.288989 seconds.
看起來 MATLAB 內置sum函式的運行速度比回圈慢,正如這里for簡要討論的那樣。新版本的 MATLAB(至少從 R2019a 起)中的陣列初始化速度很快,但如果您使用的是舊版本,則可以以不同的方式初始化它。b=zeros(Nz,1)
然而,@Ander 的建議值得注意,我們可以遵循他的建議,但使用for回圈進行總結:
tic
Nz = 3636 ; % Number of rows
Nx = 910 ; % Number of columns
R = rand(Nz, Nx, 2) ;
alpha = 3 ;
for ii = Nx - 1 : -1 : 2
b = zeros(Nz, 1) ;
S1 = 0 ;
for mm = Nx - 1 : -1 : ii % Summation
S = S (R(jj 1, mm, 2) R(jj 1, mm, 1)) - 2 * (R(jj, mm, 2) R(jj, mm, 1)) (R(jj - 1 , mm, 2) R(jj - 1, mm, 1)) ;
end
for jj = 2 : Nz - 1
S = 0 ;
for mm = Nx - 1 : -1 : ii % Summation
S = S (R(jj 1, mm, 2) R(jj 1, mm, 1)) - 2 * (R(jj, mm, 2) R(jj, mm, 1)) (R(jj - 1 , mm, 2) R(jj - 1, mm, 1)) ;
end
b(1) = 2 *(R(1, ii, 1) 2 * alpha * S1) ;
b(jj) = 2 *(R(jj, ii, 1) alpha * S) ;
end
end
toc
Elapsed time is 26.911663 seconds.
If computing time is a big concern, you can create some temporary variables before the for loop to avoid some summations/multiplications. This makes the code slightly worse to read, but it runs faster:
tic
Nz = 3636 ; % Number of rows
Nx = 910 ; % Number of columns
R = rand(Nz, Nx, 2) ;
R12 = R(:,:,1) R(:,:,2);
alpha = 3 ;
Nx1 = Nx-1;
Nz1 = Nz-1;
two_alpha = 2*alpha;
for ii = Nx1:-1:2
b = zeros(Nz, 1) ;
S1 = 0 ;
for mm = ii:Nx1 % Summation
S1 = S1 R12(2, mm) - R12(1, mm) ;
end
for jj = 2:Nz1
S = 0 ;
for mm = ii:Nx1 % Summation
S = S R12(jj 1, mm) -2*R12(jj, mm) R12(jj - 1 , mm) ;
end
b(1) = 2 *(R(1, ii,1) two_alpha*S1) ;
b(jj) = 2 *(R(jj, ii,1) alpha*S) ;
end
end
toc
Elapsed time is 12.202103 seconds.
Since the limits of summation extend up to N-1, creation of the Nx1 and Nz1 variables avoids three subtractions on each ii-loop. The two_alpha variable also avoids one multiplication per iteration. Creation of the R12 also speeds up calculation, since in the original code this sum on the 3rd dimesion appears on every line of the summations.
I tried to also create a two_R12=2*R12 variable for the mm-loop, but it runs slower since the for loop must access elements on two different variables.
您還說該b變數用于求解線性系統q = D\b。這也會減慢計算速度。如果D矩陣是常數,考慮分解它,以加快計算速度。
uj5u.com熱心網友回復:
大多數計算都是重復求和,可以轉換為cumsum. 結果b是一個矩陣,您可以簡單地使用它一次D\b計算q所有列。
R = R(:, 2:end-1, :);
sm = sum(R, 3);
S1 = cumsum(sm(2, :) - sm(1, :), 2, 'reverse')
S = cumsum(conv2(sm, [1;-2;1], 'valid'), 2, 'reverse')
b = [2 .* (R(1, :, 1) 2 * alpha * S1);
2 .* (R(2:end-1, :, 1) alpha * S);
zeros(1, size(R, 2))];
q = D\b;
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/426517.html
標籤:performance matlab optimization vectorization computation
上一篇:如何顯示數字答案而不是計算?
下一篇:MATLAB運算子的排序
