MATLAB (GNU Octave) はベクトル演算で本領を発揮する

Renewed my understanding that MATLAB displays its real ability by vector calculations.

取引先の依頼で、信号処理シミュレーションコードの設計をしています。どう見ても MATLAB(実際にはクローンの GNU Octave)向きの処理に思えたので MATLAB で設計したのですが、ベクトル演算化が面倒な部分があったので、まずは暫定的にスカラ値を求める関数を書き、arrayfun() 処理を使って、その関数をベクトル(配列)に適用する実装をしていました。

具体的には、あるスカラ値 t に関してその関数値(スカラ)を返す関数 foo() を実装したとしましょう。例えば、

function f = foo(t)
    f = sin(t);
end

のような感じです。

そして、いま時間ベクトル t1 があるとき、t1 それぞれの要素に foo() を適用したベクトル f1 を得たければ、

f1 = arrayfun(@foo, t1);

のようにして結果を得られます。(これはあくまでも例であり、もしここで本当にサイン関数が必要ならば、そのままベクトル演算することができます。つまり、f1 = sin(t1) のように。)

さて。ここでの問題は、MATLAB が arrayfun() 処理をするには、関数 foo() の処理をベクトル t1 の全てのスカラ要素に対して呼出しを繰り返さなくてはならないことです。MATLAB は、たとえばベクトル a と b の要素をそれぞれ掛け合わせるような処理

x = a .* b;

は内部的に高速処理できますが、arrayfun() はベクトルの各スカラ要素に対して関数を「実際に」呼び出さなくてはならないので、MATLAB はその本領を発揮できないのです。結果として、この処理はとても遅いものになります。

今日は重い腰を上げて、関数 foo() がベクトル t を受け取ってベクトル f を返すように書き換えてみました。つまり、関数 foo() の代わりに foo_vect() を

function f_vect = foo_vect(t_vect)
    f_vect = ほげほげ;
end

のように作り、arrayfun() を使わずに直接

f1 = foo_vect(t1);

と書けるように変更したのです。するとどうでしょう?  なんと、GNU Octave での実行速度が 100倍程度に高速化しました。(もちろん、どれくらい高速化するかは関数 foo() の実装に依存すると思います。)

MATLAB を普段御利用の方は「何をいまさら」と思われるでしょう。また、MATLAB と GNU Octave の内部実装の違いから、本物の MATLAB ではそこまでの差は出ないかも知れません。しかしながら、MATLAB の本領を発揮するためにはベクトル演算で設計することがいかに重要か、ということを痛感した次第です。