19.1 Basic Vectorization

Last-modified: 2025-03-03 (月) 21:36:47

19.1 基本的なベクトル化

第一近似として、ベクトル化の目標は、ループを避け、全配列演算を使用するコードを書くことである。簡単な例として

for i = 1:n
 for j = 1:m
   c(i,j) = a(i,j) + b(i,j);
 endfor
endfor

はるかに単純な

c = a + b;

これは単に書くのが簡単というだけでなく、内部的に最適化するのもはるかに簡単です。Octaveは、この処理を基礎となる実装に委譲します。この実装は,他の最適化の中でも,特別なベクトル・ハードウェア命令を使用することができますし,加算を並列に実行することも考えられます。一般にコードがベクトル化されている場合、基礎となる実装はより高速な実行を達成するためにどのような仮定を置くことができるかについて、より多くの自由を得ることができます。

これは 「チープなボディを持つループでは特に重要です。多くの場合、許容できるパフォーマンスを得るためには最も内側のループだけをベクトル化すれば十分です。一般的な経験則では、ベクトル化された本体の「次数」は、それを囲むループの 次数」以上でなければなりません。

単純な例として

for i = 1:n-1
 a(i) = b(i+1) - b(i);
endfor
write
a = b(2:n) - b(1:n-1);

これは、インデックス変数をループする代わりに、インデックス付けに配列を使うという重要な一般概念を示しています。
インデックス式を参照のこと。
また、ブーリアンインデックスも積極的に使いましょう。ある条件をテストする必要がある場合、この条件もブーリアンインデックスとして書くことができます。例えば

for i = 1:n
 if (a(i) > 5)
   a(i) -= 20
 endif
endfor
write
a(a>5) -= 20;

a > 5これはブールインデックスを生成する という事実を利用します。

ループを回避するために、可能な限り要素ごとのベクトル演算子を使用します ( や などの演算子.*) .^。算術演算子を参照してください。

また、ループや不要な中間メモリ割り当てを避けるために、これらの要素演算子でブロードキャストを利用する。ブロードキャストを参照してください。

可能であれば組込み関数やライブラリ関数を使いましょう。組み込み関数やコンパイル済み関数は非常に高速です。m-fileライブラリ関数であっても、すでに最適化されているか、将来のリリースでさらに最適化される可能性が高くなります。

 例えば、

a = b(2:n) - b(1:n-1);
は
a = diff (b);

ほとんどのOctave関数は、ベクトルや配列の引数を念頭に置いて書かれています。もしあなたが非常に単純な操作でループを書くようでしたら、そのような関数がすでに存在している可能性があります。以下の関数はベクトル化されたコードで頻繁に使用されます:

Index manipulation

find

sub2ind

ind2sub

sort

unique

lookup

ifelse / merge

Repetition

repmat

repelems

Vectorized arithmetic

sum

prod

cumsum

cumprod

sumsq

diff

dot

cummax

cummin
vShape of higher dimensional arrays

reshape

resize

permute

squeeze

deal