web-dev-qa-db-ja.com

最終的には高価です

関数を終了する前にリソースのクリーンアップを行う必要があるコードの場合、これら2つの方法でパフォーマンスを大幅に向上させることはできますか。

  1. すべてのreturnステートメントの前にリソースをクリーニングする

    void func()
    {
        login();
    
        bool ret = dosomething();
        if(ret == false)
        {
            logout();
            return;
        }
    
        ret = dosomethingelse();
        if(ret == false)
        {
            logout();
            return;
        }
    
        dootherstuff();
    
        logout();
    }
    
  2. Finallyブロックでリソースをクリーニングする

    void func()
    {
        login();
    
        try
        {
            bool ret = dosomething();
            if(ret == false)
                return;
    
            ret = dosomethingelse();
            if(ret == false)
                return;
    
            dootherstuff();
    
        }
        finally
        {
            logout();
        }
    }
    

サンプルプログラムでいくつかの基本的なテストを行いましたが、それほど大きな違いはないようです。私はこれを行うfinally方法を非常に好みますが、それが大きなプロジェクトでパフォーマンスに影響を与えるかどうか疑問に思っていました。

14
user93353

どのくらい遅いJava例外? に示されているように)try {} catch {}の遅いことが例外自体の状態の範囲内にあることがわかります。

例外を作成すると、ランタイムからコールスタック全体がフェッチされますが、ここで費用がかかります。例外を作成しない場合、これは時間の増加veryのみです。

この質問の例では、例外はありません。例外が発生することを期待することはできません。これらは作成されません。代わりに、ここにあるのは、finallyブロック内のリソース割り当て解除を処理するtry {} finally {}です。

したがって、質問に答えるために、いいえ、例外を使用しないtry {} finally {}構造内に実際の実行時の費用はありません(見たように、前代未聞ではありません)。 が何であるか高価になる可能性があるのは、コードを読み取ってこれが一般的なコードスタイルではないことを確認し、コーダーが他のことについて考えなければならない場合のメンテナンス時間ですこのメソッドでは、returnの後、前の呼び出しに戻る前に発生します。


すでに述べたように、保守はこれを行う両方方法の引数です。念のため、検討した結果、最終的には私のアプローチが優先されます。

誰かに新しい言語構造を教えるメンテナンス時間を考慮してください。 try {} finally {}の表示は、Javaコードで頻繁に表示されるものではないため、人々を混乱させる可能性があります。=で少し高度な構造を学習するには、ある程度のメンテナンス時間が必要です= Java人々が見慣れているものよりも。

finally {}ブロックalwaysが実行されます。そして、これがあなたがそれを使うべき理由です。誰かが適切な時間にログアウトを含めるのを忘れたり、不適切な時間にそれを呼び出したり、呼び出し後に戻り/終了するのを忘れたりして、2回呼び出されるようになっている場合は、nonfinallyアプローチをデバッグするメンテナンス時間も考慮してください。これには非常に多くの潜在的なバグがあり、try {} finally {}を使用することは不可能です。

これら2つのコストを比較検討する場合、try {} finally {}アプローチを使用するのは、メンテナンス時間ではなく高くつきます。人々はtry {} finally {}ブロックが他のバージョンと比較されたミリ秒数または追加のjvm命令の数について気にすることができますが、リソースの割り当て解除に対処するための理想的とは言えない方法でデバッグに費やす時間も考慮する必要があります。

メンテナンス可能なコードを最初に記述し、できればバグが後で記述されないようにします。

23
user40980

https://stackoverflow.com/questions/299068/how-slow-are-Java-exceptions

この質問で受け入れられた回答は、関数呼び出しをtry catchブロックでラップすると、裸の関数呼び出しに比べてコストが5%未満であることを示しています。実際に例外をスローしてキャッチすると、ランタイムはベア関数呼び出しの66倍以上に膨らみます。したがって、例外デザインが定期的にスローすることを期待している場合は、それを回避しようとします(適切にプロファイルされたパフォーマンスクリティカルなコードで)。ただし、例外的な状況がまれである場合、それは大した問題ではありません。

5
stonemetal

最適化する代わりに、コードが何をしているかを考えてください。

Finallyブロックの追加は別の実装です。つまり、すべての例外でログアウトします。

具体的には、ログインが例外をスローした場合、2番目の関数の動作は最初の関数とは異なります。

ログインとログアウトが実際に関数の動作の一部ではない場合、メソッドは1つのことと1つのことをうまく実行することをお勧めします。

    void func()
    {
            bool ret = dosomething();
            if(ret == false)
                return;

            ret = dosomethingelse();
            if(ret == false)
                return;

            dootherstuff();

    }

また、ログイン/ログアウトを管理するコンテキストに既にカプセル化されている関数内にある必要があります。これらは、メインコードが実行しているものとは根本的に異なっているためです。

あなたの投稿はJavaとしてタグ付けされています。これは私にはあまり詳しくありませんが、.netではこれに使用ブロックを使用します:

つまり:

    using(var loginContext = CreateLoginContext()) 
    {
            // Do all your stuff
    }

また、ログインコンテキストには、ログアウトしてリソースをクリーンアップするDisposeメソッドがあります。

編集:実際には質問に答えませんでした!

私には測定可能な証拠はありませんが、これがこれ以上高価になることや、時期尚早の最適化に値するほど十分に高価であることは期待できません。

質問には答えませんが、OPには役立つと思いますので、残りの答えはそのままにしておきます。

4
Michael

前提:C#コードで開発しています。

簡単に言えば、try/finallyブロックを使用してもパフォーマンスに大きな影響はありません。例外をスローしてキャッチすると、パフォーマンスが低下します。

より長い答えはあなた自身のために見ることです。生成された基になるCILコード( たとえば、レッドゲートリフレクター )を見ると、基になるCIL命令が生成され、その影響がわかります。

1
Michael Shaw