研究室でのScilabの演習で画像処理をやっていたのだが、
その中でガウシアンフィルタを生成する処理を書いていた。
そこでScilab流のやり方を聞いたのだが、一般的なプログラミング言語では非効率に見えるやり方がScilab流だそうで。
一般的なプログラミング言語の例でやればこんな風になるよね。
gaussian=zeros(size,size);
for i=1:size
for j=1:size
gaussian(j,i)=exp(((j-size/2)^2+(i-size/2)^2)/2/s);
end
end
gaussian=gaussian/sum(gaussian);
forでループ回して計算するよねって。
最後に総和が1になるように正規化してるが、これはsumを使ってやれば簡単なので、そこはScilabらしい書き方だが。
ところでScilabではこれよりもこう書いた方が速いそうだ。
[Y,X]=ndgrid(linspace(-size/2,size/2,size),linspace(-size/2,size/2,size));
gaussian=exp((Y.^2+X.^2)/2/s);
gaussian=gaussian/sum(gaussian);
ずいぶんコンパクトにできている。
なにをしてるかという話だが、ndgridを使ってsize×sizeの行列を2つ生成している。
これでYは –size/2からsize/2の Y座標の入った行列、XにはX座標の入った行列が生成される。
この2つの行列を使って、各要素で exp((x^2+y^2)/2/s を計算させると。各要素へのべき乗は .^ で書けて、
expに行列を入れれば各要素にexpが適用される。
Scilabは行列が基本だからこう書けばたちまち計算が出来てしまう。
やってることは一緒で、後者の方がコンパクトにできていることがわかる。
ただ、実際の処理を考えてみると、X,Yといった行列を一旦生成する必要があり、後者の方が計算量は多いように思える。
それでもforを使うより、行列の計算に落とし込んだ方がScilabでは速いらしい。
実際、もうちょっと重い処理、具体的にはゼロクロス点の検出で比較してみると20倍ぐらいの速度差があったようだ。
なんでだ?
調べてみるとこんな記述がひっかかった。
タイトルの通りです。Scilabに限らずインタプリタ処理をする言語では,ループ計算は遅くなります。
(Scilabの使用法3-ループ計算は遅いので,できるだけ使わない (琉球大学 理学部 物質地球科学科 物理系 NMR物性研究室) )
計算が重いのではなく、構文解析が重いのだという。
確かに純粋なインタプリタならforループではループごとに構文解析→実行の処理を繰り返す必要がある。
それに対して、行列の計算で書けば、計算はともかく、構文解析は一度行えばそれでよい。
計算量が少々増えても、forで構文解析を延々と繰り返すよりもましということらしい。
ただ、インタプリタ型の言語と紹介されることも多い、Perl・Ruby・Pythonなどは、
実行するとき、まずコンパイルを行ってからプログラムを実行している。
ユーザーはコンパイルを意識することはないが、内部的には コンパイル→実行 の流れを踏んでいるわけだ。
コンパイルといってもネイティブコードを生成するようなものではなくて、中間コードを生成する処理なんだけど、
構文解析はコンパイル時に1回やるだけで済むし、最適化を施すこともできる。
とにかく、実行前にコンパイルする方がずっと高速に動作できるので、インタプリタ型の言語と紹介されるものもそうなっていることが多い。
だから、インタプリタ型の言語だからforループが遅いというのは一般的には正しい説明とは思わないが、
事前にコンパイルを行わず、本当に逐次的にソースを読み実行する場合は確かに正しい。
Scilabでは高速とされる記述では、とにかく行列を山ほど生成することになる。
なのでメモリ消費が激しいそうだ。
ScilabじゃなくてMATLABの話だが、大量のデータを処理するときメモリが足らなくて困っていると言っている人がいた。
Cで書けばメモリ不足の問題は解決するが、しかしMATLABのライブラリが使えず、特にGUIの面で不便が生じる。
そういうわけで困っていたようだが。
行列計算への強さ、ライブラリの充実を求めてScilabを使うわけだが、
プログラミング言語として見たときの弱さがこういうところに現れていたのかなと。
確かに便利なんだけど、処理が遅くてやってられないとか、処理できるデータ数が限られるとか、
そういうことになるぐらいなら、Perl・Rubyだとか賢い言語を使うか、Cで書いてネイティブコードでガシガシ動かすのがよいように思える。
Scilabでプロトタイプを作って、場合によっては他の環境に動かすと、そういうことも考慮に入れるべきかなと。
Scilabで関数1つでできていたことを、自分で記述する必要もあるかもしれないけど。