最適化
ちょっと別件を調べてたら、こんなの見つけた。
Visual Basic .NET でのパフォーマンスの最適化
ループの記述で、
Try ブロック内のループの数を最小化し、ループ内の Try ブロックの数を最小化するようにしてください。長いループを使用すると、構造化例外処理のオーバーヘッドが大きくなります。
For、Do、または While のどのループが一番効率的であるかについては、さまざまな見解があります。事前テストでは、これらのループ間には大きな差や一貫した差は見られませんでした。したがって、これらのループの種類を選択するにあたってパフォーマンスを考慮する必要はありません。
とあるので、例外処理のコストを考慮した方がいいということだけど。
例外処理のスタックが入るから、そのためのオーバーヘッドがありますよ、ってことか。
Javaも同じような気がするけど、そのオーバーヘッドってどの程度なんだろ?
以前測定してみたとき、大して影響ないと思ったんだけど。
これはエラーが発生した場合の検出方法の比較ってことで、両方ともtry-catchが入ってる。
エラーの検出方法として「例外をthrowする場合」と「エラーの状態を判定して戻り値で戻す」場合のコードで、『エラーが発生しない状態』で比較をしてみればいいのか。
例外処理のコスト、の続き(Java編)
以前のコードに若干手を入れて、「try-catch文のコスト」を例外の検出方法という観点で測定してみた。
・エラーの状態を戻り値で戻して、if文で検出する
・例外をthrowして、try-catch文で例外を検出する
で、下記のコードで測定してみた。
1億回のループでの結果は、
処理時間[ms] | |
---|---|
if文 | 6,671 |
try-catch文 | 6,500 |
となり、何回か測定したが、約100〜200[ms]程度「if文で検出する」方が遅いという結果となった。
try-catch文によるスタックのオーバーヘッドより、if文の評価の方がコストが高いってことでいいかな?
というか、if文もブロックの部分がスタックで、オーバーヘッドにかかるコストは一緒とかか?
何回もループして、通常は「正常」で、極々稀に「例外」が出るような場合は、「try-catch文」の方がトータルコストは有利。
但し、例外検出後の処理スピードが要求される場合は、例外を生成するコストの分だけ「try-catch文」の方が不利。
って感じか。
.NET系はまた機会があったら…
どのパターンでも、極力同等の評価演算行われるように改造してみた。
public class TryCatchTest { public TryCatchTest() { super(); } public static void main(String[] args) { TryCatchTest test = new TryCatchTest(); test.test1(0); //test.test1(1); // 例外が発生しない場合のみ測定 //test.test1(2); // 例外が発生しない場合のみ測定 } private void test1(int mode) { //long loop = 1000000L; long loop = 100000000L; // 差が見えないので // 戻り値で例外を検出する場合(if文で検出) long start1 = System.currentTimeMillis(); int err1 = 0; for ( long i = 0 ; i < loop ; i++ ) { if (method1(i, mode) == -1) { err1++; } } long end1 = System.currentTimeMillis(); // try-catchで例外を検出する場合(try-catchで検出) long start2 = System.currentTimeMillis(); int err2 = 0; for ( long i = 0 ; i < loop ; i++ ) { try { method2(i, mode); } catch (RuntimeException e) { err2++; } } long end2 = System.currentTimeMillis(); System.out.println("[" + mode + "]int " + err1 + "\t" + (end1 - start1)); System.out.println("[" + mode + "]RuntimeException " + err2 + "\t" + (end2 - start2)); } // 戻り値で例外を検出する場合 private int method1(long arg, int mode) { long ans = arg % 2; if (((mode == 1) && (ans == 0)) || (mode == 2)) { return -1; } return 0; } // try-catchで例外を検出する場合 private void method2(long arg, int mode) { long ans = arg % 2; if (((mode == 1) && (ans == 0)) || (mode == 2)) { throw new RuntimeException(); } return; } }