プロが教えるわが家の防犯対策術!

大きなプログラムを書く際、プログラムの見通しが良くなり、後から修正しやすくするため、
同じ計算が複数回出る際には関数を多用したものを作成してきました。

ところが、プログラムの計算速度を上げたいと思い、計算時間を比較してみたところ、
関数を使わない方が圧倒的に速くなることが分かりました。




Matlabを使って、

for j = 1:100000
for i = 1:1000

y = i*i * j *j;

end
end

の計算を行った際の計算時間と、ループ内の計算を関数で置き換えた
for j = 1:100000
for i = 1:1000

y = zikan_sokutei_function ( i, j );

end
end

function [ kei ] = zikan_sokutei_function ( i, j )
kei = i * i * j * j;
end

の計算を行った時間は、
それぞれ、0.5秒と1分7.3秒、という結果になりました。
つまり関数で置き換えたことで、計算時間が実に135倍も増大したということです。

上記と同じプログラムを使ってVBAで比較したところ、
それぞれ、8.29秒と20.70秒、という結果になりました。
つまり関数で置き換えたことで、計算時間が2.5倍も増大したということです。


また、関数を使わない場合には、Matlabが最速で、関数を使う場合にはVBAが最速であるという結果になっています。





これは関数を全く使わない方が、圧倒的に高速に計算を実行できるという結果になっていますが、
こういうものなのでしょうか?
2倍程度なら許容範囲ですが、100倍以上も遅くなるのであれば、
関数は全く使うべきではない、ということになりますが、なぜこれほどまでに時間がかかるのでしょうか?



Matlabの方がVBAよりも数値計算に適しているため、VBAよりも実行速度は速くなるべきだと思いますが
関数を使った場合には、これが逆転してしまうという、かなり腑に落ちない結果となりましたが、
こういうものなのでしょうか?

A 回答 (8件)

とにかく、matlabでの、唯一絶対の基本原則は、「for文を回しては絶対に駄目」です。


matlabで、ループ回数がものすごく多いfor文を書くと、処理時間が、行列演算の100倍どころか1000倍遅いとか平気でなります。

例えば、for文を使ったプログラムが
y=0;
for j = 1:100000
 for i = 1:1000
  y = y + i*i * j *j;
 end
end
だとしたら、
(質問文のプログラムは、結果として何も計算していないの、少し変えました。)

matlabでは、
y = sum((1:1000).^2)*sum((1:100000).^2);
という行列演算として一行で記述してください。
同じ結果が、おそらく一瞬で出てくるはずです。

matlabの最適化機能はあまり頭が良くないので、かなり単純な構造じゃないと、ループを行列演算に自動変換してくれません。
なんで、基本的には、ユーザーが上記のように、明示的にきちんとループではなくて行列演算の形で記述する必要があります。
    • good
    • 0

#4です。


ちょっと、きちんと言いたいことの本質が伝わらないかもと思ったので補足。

matlabに関して言えば、100倍遅くなった原因は、
・関数呼び出しにしたから
ではなくて、
・ループ(for文)を使ったから
です。

一見、両方とも、ループが存在しているように見えますが、関数版でないほうは、matlabが内部でプログラムを自動的に解析して、内部で、ループではなくて行列計算になるように変更して実行されたため速いのです。
関数版のほうは、最適化が利かなくなってしまったため、実際にループとして実行されたので、劇遅になってしまったわけです。


>2倍程度なら許容範囲ですが、100倍以上も遅くなるのであれば、
>関数は全く使うべきではない、ということになりますが、なぜこれほどまでに時間がかかるのでしょうか?

matlabに関して言えば、関数呼び出し自体は当然使用してもよいですが、大規模なループ(for文)は使用すべきではありません。(それこそ100倍遅くなります)
    • good
    • 0
この回答へのお礼

皆さんご回答ありがとうございます。

遅くなる原因は理解することができました。

もう一つ関連した質問をお聞きしたいのですが、
関数に変数のやり取りをしているために遅くなる、というのであれば、
グローバル変数を使ってやれば、引数のやり取りだけはなくなり、その分速くなるのではないかと思い
試してみました。

計算回数を一桁下げて
for j = 1:10000
for i = 1:1000
とした上で、

VBAで
public i, j
をsubの前に書いて実行すると、
iとjをグローバル変数としていない場合で、2.00秒、した場合で1.72秒となり、
若干だけですが、速度が向上しました。


一方で、matlabで
global i j;
を定義してから同じことをやってみると、
iとjをグローバル変数としていない場合で、6.5秒、した場合で2分12.2秒となり、
なぜかVBAとは反対に、20倍も遅くなってしまうという結果になりました。

この結果は、No.7さんが仰るようにforループを行列としてmatlabが最適化したと考えたとしても、
関数と変数のやり取りをしているためであると、考えたとしても説明がつきません。


なぜmatlabではこのようにグローバル変数を用いた方が20倍も計算速度が遅くなるのでしょうか?

お礼日時:2013/02/22 20:59

> 2倍程度なら許容範囲ですが、100倍以上も遅くなるのであれば、


> 関数は全く使うべきではない、ということになりますが、...

それは極論ですね。速度のボトルネックの多くはプログラムのごく一部です。速度のチューニングは後回しにして、関数を多用する質問者さんの従来のスタイルが正しいと思います。

逆に関数を使わないと、アルゴリズムのボトルネックを探すことが難しくなるでしょう。プロファイラ等で時間計測することが難しくなってしまうので。

しかし最適化すべき部分と期限が決まったなら、どんな手段もためらうべきではないとは思います。
    • good
    • 0

お疲れ様です。


抽象的な説明ですが参考に為れば幸いです。


例えば次のような関数が有ったとします。

add_abc = abc( a, b, c )

function abc( a, b, c ){
  return( a + b + c )
}

これを呼び出す一連の処理は
(注:値の受け渡しにstackを利用する前提です)
1.呼び出し元で、aの値をstackに格納
2.呼び出し元で、bの値をstackに格納
3.呼び出し元で、cの値をstackに格納
4.呼び出し元で、abcを呼び出す
5.関数側で、aの値を取り出す
6.関数側で、bの値を取り出す
7.関数側で、cの値を取り出す
8.関数側で、演算 a + b + c を行う
9.関数側で、結果(return値)を汎用レジスタなどに置く
10.関数側で、return命令で呼び出し元に帰る
11.呼び出し元で、cの値をstackから削除
12.呼び出し元で、bの値をstackから削除
13.呼び出し元で、aの値をstackから削除
14.呼び出し元で、結果(return値)をadd_abc変数に格納する
、を行っています。

1~7、9~13までが呼び出しに伴うオーバーヘッドとなります。
8と14は関数を使わなければそのまま行っている事ですよね。
簡単な演算なのでオーバーヘッドが物凄く多く感じますが
処理内容によっては無視できる範囲になります。

今回のように呼び出し回数が多い場合にはオーバーヘッドばかり増えますよね。
そこのところを考えて判断してください。
    • good
    • 0

どう書くのが速いか、言語と、コンパイラやインタプリタの最適化機能によるので一般論では言えません。


通常は、まったく同じ内容を関数化すれば、最適化が効き難くなる分だけ遅くなることが多いとは思いますが、場合によっては、関数化することで、コンパイラの中での最適化用で考えないと行けないスコープが狭まることで、逆に最適化が効きやすくなって高速化する場合もあります。

その上で、Matlabに限定した話ですが。
まず、Matlabでは、基本的に、for文などのループは劇遅なので使ってはいけません。
ループで行列の要素毎に計算するのではなく、必ず行列全体をまとめて計算するようにします。こうすると、Matlabはかなり速いです。(それこそ下手にCやfortranで自分で書くよりずっと速い)
で、今回の場合、非関数版のプログラムのほうでは、おそらくMatlabのJITコンパイラが、ループを行列計算に最適化して実行したのでしょう。
それに対して関数版では、最適化機能が効かずに実際にループとして実行されたのでしょう。
Matlabのループは本当に劇遅なので、VBAより遅いということもありえる話だと思います。
とくに、Matlab でループの中で、行列のサイズを毎回変更するとかすると、信じられないくらいに遅いですよ。
    • good
    • 0

関数を使わないよりは使ったほうが時間が掛かるのは当然ですが、単純に倍率で判断しないほうがいいでしょう。



示された例の場合は、関数が100000×1000回呼び出されたことによる増大ですから、
比率ではなく、差で比較したほうが適切です。

Matlabなら、1分6.8秒
BVAなら、12.41秒
増えたということです。

今回は、kei = i * i * j * j; という単純な計算だから何倍にもなっただけで、
これがもっと時間の掛かる処理、例えば、100000×1000回やって10分掛かるような処理でも、その差は同じですから、
Matlabは、10分と11分6.8秒
BVAなら、10分と10分12.41秒
ということになり、倍率としてはそれほど違いはないでしょう。

1時間掛かるような処理なら、そのくらいの差はほとんど気にならなくなります。
    • good
    • 0

MATLABは使ったことがないですがインタプリタとしては遅いようです。

MATLABを純粋に演算機能だけ比べればVBAよりも速いのも情報通りです。
関数呼び出しは、処理後に続きに戻る情報を記録するため基本的に遅いのですが、MATLABは特別遅いのかもしれません。

関数呼び出しを高速に実行するにはコンパイラ系の言語を使ったほうが良いので、FORTANとかであれば問題がほぼありません(若干は速度低下します)。
MATLAB Compilerってもの有る見たいですから使えるのなら使ってみてはどうでしょうか?
http://www.mathworks.co.jp/products/compiler/
    • good
    • 0

一般的に……関数(というかサブルーチン)を使用した場合は、


「サブルーチンから戻る時の戻り先」
「サブルーチン内で使用するローカル変数の領域の確保」
などを記憶して、
「制御をそのサブルーチンの開始位置に飛ばす」
という処理が入ります。

単純なループだけでしたらサブルーチン化した場合に上記のオーバーヘッドが重くなります。
# 言語仕様によるところもあるかと思われますが。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!