目次

6. 2重for文の配列演算化

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)。

表6-1 2重for文の処理内容
記号処理内容
(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

表6-2 for文の配列演算化の計算時間 (n=2000、実数倍精度)
処理内容MATLABScilabOctaveC (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秒非対応

表6-3 for文の配列演算化の計算時間 (n=20000、実数倍精度)
処理内容MATLABScilabOctaveC (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)