S_a_k_Uの日記みたいなDB

~サクゥーと呼ばないで~

最適化

ちょっと別件を調べてたら、こんなの見つけた。
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;
    }
    
}