MATLAB/Scilab/Octaveではfor文をそのまま記述すると時間がかかるので、
これを配列演算化することが重要です。
リスト6-1に示す2重for文の4通りの処理法の計算時間を測定します。
リスト6-1 2重for文の配列演算化のソースコード (for2.m)
1 clear all; 2 3 n = 2000; 4 a = rand(n, 1); 5 b = rand(n, 1); 6 c = rand(n, 1); 7 d = rand(n, 1); 8 e = zeros(n, n); 9 10 tic; % (1) 11 for i = 1 : n 12 for j = 1 : n 13 e(i, j) = sin((a(i) - b(j)) * (c(i) + d(j))); 14 end 15 end 16 toc; 17 fprintf('%d %f\n', n, sum(e(:)) / n^2); 18 19 tic; % (2) 20 for j = 1 : n 21 for i = 1 : n 22 e(i, j) = sin((a(i) - b(j)) * (c(i) + d(j))); 23 end 24 end 25 toc; 26 fprintf('%d %f\n', n, sum(e(:)) / n^2); 27 28 tic; % (3) 29 for i = 1 : n 30 e(i, :) = sin((a(i) - b(:)) .* (c(i) + d(:))); 31 end 32 toc; 33 fprintf('%d %f\n', n, sum(e(:)) / n^2); 34 35 tic; % (4) 36 for j = 1 : n 37 e(:, j) = sin((a(:) - b(j)) .* (c(:) + d(j))); 38 end 39 toc; 40 fprintf('%d %f\n', n, sum(e(:)) / n^2);
それぞれの処理方法は表6-1の通りです。
MATLAB/Scilab/Octaveでは行列の格納方法はcolumn方向(列方向)が外側になります(column major)。
これはFortranと同じです。
一方、C/C++ではrow方向(行方向)が外側になります(row major)。
記号 | 処理内容 |
---|---|
(1) | i,jの2重for文(外ループ=row、内ループ=column) |
(2) | j,iの2重for文(外ループ=column、内ループ=row) |
(3) | iのfor文、jについては配列演算(外ループ=row、内ループ=column) |
(4) | jのfor文、iについては配列演算(外ループ=column、内ループ=row) |
表6-2と表6-3にn=2000とn=20000の計算時間を示します。
表6-2からScilabとOctaveでは(1)と(2)の2重for文は非常に時間がかかることがわかります。
それ以外の傾向は問題のサイズを大きくした表6-3からわかります。
最も速いケースに色をつけています。
MATLABでは配列に連続的にアクセスする(2)と(4)が速くなります。
配列演算化し、配列に連続にアクセスする(4)が最も速くなります。
(Cの1スレッドより速くなります)
ScilabとOctaveでは、(3)と(4)で配列演算化すると大幅に速くなりますがMATLABには及びません。
特に(4)で配列に連続にアクセスしてもあまり速くなりません。
Cではrow majorであるために(1)は(2)と比べて大幅に速くなります。
(ソースコード:for2.c)
処理内容 | MATLAB | Scilab | Octave | C (VC++) 1スレッド |
---|---|---|---|---|
(1) | 0.072秒 | 20.976秒 | 50.513秒 | 0.014秒 |
(2) | 0.053秒 | 21.070秒 | 50.369秒 | 0.058秒 |
(3) | 0.039秒 | 0.114秒 | 0.199秒 | 非対応 |
(4) | 0.021秒 | 0.093秒 | 0.185秒 | 非対応 |
処理内容 | MATLAB | Scilab | Octave | C (VC++) 1スレッド |
---|---|---|---|---|
(1) | 20.56秒 | 未測定 | 未測定 | 1.56秒 |
(2) | 3.61秒 | 未測定 | 未測定 | 18.60秒 |
(3) | 10.16秒 | 18.92秒 | 25.45秒 | 非対応 |
(4) | 0.59秒 | 7.78秒 | 15.80秒 | 非対応 |
MATLABの配列演算化について
リスト6-1の(3)と(4)では配列演算化を用いました。
配列演算化とはfor文で処理せずに、配列の変数のまま四則演算と関数演算を行うことです。
配列の要素同士の足し算と引き算はそのまま記述します。
掛け算と割り算はそれぞれ".*"と"./"に変更します。
配列全体は"(:)"で表します。
関数を用いた計算も同様です(配列の関数値は関数値の配列になります)。
以上の方法により、for文を取り除いて少しの変更で対応することができ、
これによって計算時間を大幅に短縮することができます。
また、行列を参照するときは行方向に連続してアクセスすることが大切です。
(column major)