Javaのパフォーマンスに対する例外の影響は何ですか?


496

質問:Javaでの例外処理は実際には遅いですか?

従来の知識と多くのGoogleの結果は、Javaの通常のプログラムフローに例外的なロジックを使用するべきではないと述べています。通常、2つの理由が挙げられます。

  1. それは本当に遅いです-通常のコードよりも桁違いに遅いです(与えられた理由は異なります)、

そして

  1. 例外的なコードで処理されるのはエラーのみであることを人々が期待するので、それは厄介です。

この質問は#1についてです。

例として、このページではJava例外処理を「非常に遅い」と説明し、スローを例外メッセージ文字列の作成に関連付けます-「この文字列は、スローされる例外オブジェクトの作成に使用されます。これは高速ではありません。」記事Javaでの効果的な例外処理は、「これの理由は、例外処理のオブジェクト作成の側面が原因であり、それによって例外のスローが本質的に遅くなります」と述べています。もう1つの理由は、スタックトレースの生成が遅くなることです。

私のテスト(32ビットLinuxでJava 1.6.0_07、Java HotSpot 10.0を使用)は、例外処理が通常のコードより遅くないことを示しています。コードを実行するループでメソッドを実行してみました。メソッドの最後で、ブール値を使用してreturnまたはthrowのどちらを示すかを示します。このように、実際の処理は同じです。メソッドをさまざまな順序で実行し、テスト時間を平均化してみました。JVMがウォームアップしているのではないかと思いました。私のすべてのテストで、スローは少なくともリターンと同じくらい高速でしたが、高速ではなかった(最大3.1%高速でした)。私のテストが間違っている可能性は完全に受け入れられていますが、コードのサンプル、テストの比較、またはJavaでの例外処理が実際にあることを示す過去1〜2年の結果には、何も見たことがありません。スロー。

この道をたどるのは、通常の制御ロジックの一部として例外をスローするために使用する必要があるAPIでした。使い方を修正したかったのですが、今はできないかもしれません。代わりに私は彼らの前向きな考え方で彼らを賞賛しなければなりませんか?

論文「ジャストインタイムコンパイルでの効率的なJava例外処理」では、例外ハンドラーが存在するだけで、例外がスローされない場合でも、JITコンパイラーがコードを適切に最適化して速度を低下させるのを防ぐのに十分であると著者らは述べています。 。この理論はまだテストしていません。


8
あなたが2)について尋ねていなかったのは知っていますが、プログラムフローの例外を使用することはGOTOを使用することよりも優れていないことを本当に認識する必要があります。ゴトを擁護する人もいれば、話していることを擁護する人もいますが、どちらかを一定期間実装および保守した人に尋ねると、どちらも設計慣行を維持するのが難しいと言われます(おそらく呪われます)それらを使用する決定をするのに十分賢いと思った人の名前)。
ビルK

80
Billは、プログラムフローに例外を使用することはGOTOを使用することと同じであると主張することは、プログラムフローに条件とループを使用することはGOTOを使用することと同じだと主張することと同じです。赤いニシンです。説明してください。例外は、他の言語のプログラムフローで効果的に使用できます。たとえば、慣用的なPythonコードは例外を定期的に使用します。私は(Javaではなく)この方法で例外を使用するコードを維持することができ、維持してきました。また、コードに本質的に問題があるとは思いません。
mmalone

14
@mmaloneが通常の制御フローに例外を使用することは、パラダイムの選択がそのように行われたため Javaでは悪い考えです。Bloch EJ2を読んでください-彼はそのことを明確に述べています(項目57)exceptions are, as their name implies, to be used only for exceptional conditions; they should never be used for ordinary control flow-理由について完全かつ広範囲な説明をしています。そして彼はJava lib を書いた人でした。したがって、クラスのAPIコントラクトを定義するのは彼です。/これについてBill Kに同意します。

8
@OndraŽižka一部のフレームワークがこれを行う場合(例外ではない例外で例外を使用する)、設計に欠陥があり、破損し、言語のExceptionクラスコントラクトが破損します。お粗末なコードを書いている人がいるからといって、お粗末なコードになりません。

8
例外については、stackoverflow.comの作成者以外に間違いはありません。ソフトウェア開発の黄金律は決して単純で複雑で扱いにくいものにすることではありません。「単純な3行プログラムであるはずのものが、よくエラーチェックを行うと48行に花開くことは事実ですが、それは人生です...」これは、単純さではなく、純粋さの探求です。
sf_jeff 2015

回答:


345

例外の実装方法によって異なります。最も簡単な方法は、setjmpとlongjmpを使用することです。つまり、CPUのすべてのレジスタがスタックに書き込まれ(これにはすでに時間がかかります)、おそらく他のデータを作成する必要があります...これはすべて、tryステートメントで既に行われています。throwステートメントは、スタックを巻き戻し、すべてのレジスターの値(およびVM内の他の可能な値)を復元する必要があります。したがって、tryとthrowは同じように低速であり、かなり低速ですが、例外がスローされない場合は、tryブロックを終了するのにほとんど時間がかかりません(メソッドが存在する場合はすべてが自動的にクリーンアップされるスタックにすべてが置かれるため)。

Sunと他の人々は、これは最適ではない可能性があり、VMは時間の経過とともにますます高速になることを認識しました。例外を実装する別の方法があります。これにより、試行自体が高速になります(実際には、試行しても実際には何も起こりません。VMによってクラスがロードされたときに、発生する必要のあることはすべてすでに行われています)。 。どのJVMがこの新しいより優れた手法を使用しているのかわかりません...

...しかし、Javaで記述しているため、コードは後で特定の1つのシステム上の1つのJVMでのみ実行されますか?他のプラットフォームや他のJVMバージョン(おそらく他のベンダーのもの)で実行する可能性がある場合、高速実装も使用していると誰が言ったのでしょうか。速いものは遅いものよりも複雑であり、すべてのシステムで簡単に実現することはできません。持ち歩きたいですか?次に、例外が速いことに依存しないでください。

また、tryブロック内で行うことにも大きな違いがあります。tryブロックを開いて、このtryブロック内からメソッドを呼び出さない場合、JITは実際にスローを単純なgotoのように処理できるため、tryブロックは超高速になります。例外がスローされた場合、スタック状態を保存する必要も、スタックを巻き戻す必要もありません(キャッチハンドラにジャンプする必要があるだけです)。ただし、これは通常行うことではありません。通常、tryブロックを開いてから、例外をスローする可能性のあるメソッドを呼び出しますよね?そして、メソッド内でtryブロックを使用しただけでも、他のメソッドを呼び出さない、どのようなメソッドになりますか?数値を計算するだけですか?次に、何のために例外が必要ですか?プログラムの流れを規制するよりエレガントな方法があります。単純な数学以外のほとんどの場合、

次のテストコードを参照してください。

public class Test {
    int value;


    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    // Calculates without exception
    public void method1(int i) {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            System.out.println("You'll never see this!");
        }
    }

    // Could in theory throw one, but never will
    public void method2(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            throw new Exception();
        }
    }

    // This one will regularly throw one
    public void method3(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
        // an AND operation between two integers. The size of the number plays
        // no role. AND on 32 BIT always ANDs all 32 bits
        if ((i & 0x1) == 1) {
            throw new Exception();
        }
    }

    public static void main(String[] args) {
        int i;
        long l;
        Test t = new Test();

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            t.method1(i);
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method1 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method2(i);
            } catch (Exception e) {
                System.out.println("You'll never see this!");
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method2 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method3(i);
            } catch (Exception e) {
                // Do nothing here, as we will get here
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method3 took " + l + " ms, result was " + t.getValue()
        );
    }
}

結果:

method1 took 972 ms, result was 2
method2 took 1003 ms, result was 2
method3 took 66716 ms, result was 2

tryブロックによるスローダウンは小さすぎて、バックグラウンドプロセスなどの交絡要因を除外できません。しかし、catchブロックはすべてを殺し、66倍遅くしました!

前述のように、try / catchをすべて同じメソッド(method3)内にスローすると、結果はそれほど悪くありませんが、これは私が依存しない特別なJIT最適化です。そして、この最適化を使用しても、スローはまだかなり遅いです。だから私はあなたがここで何をしようとしているのかわかりませんが、try / catch / throwを使用するよりも間違いなく良い方法があります。


7
すばらしい答えですが、私が知る限り、それを追加したいのですが、パフォーマンスの測定にはSystem.currentTimeMillis()ではなくSystem.nanoTime()を使用する必要があります。
Simon Forsberg、2013年

10
@SimonAndréForsbergにnanoTime()はJava 1.5が必要ですが、上記のコードの記述に使用したシステムではJava 1.4しか使用できませんでした。また、実際には大きな役割を果たしません。2つの唯一の違いは、1つはナノ秒、もう1つは1ミリ秒であり、nanoTimeクロック操作の影響を受けないことです(テストコードが実行されているときにシステムプロセスを正確に変更しない限り、これは無関係です)。一般的には、あなたが正しいですがnanoTime、もちろん、より良い選択です。
メッキー2013年

2
テストは極端なケースであることに注意してください。tryブロックを含むコードのパフォーマンスヒットが非常に小さいが、はないthrow。あなたのthrowテストでは、例外を投げている時間の50%を、それが通過しますtry。これは明らかに、失敗が例外的ではない状況です。それをわずか10%に削減すると、パフォーマンスへの影響が大幅に削減されます。この種のテストの問題は、例外の使用を完全に停止することを人々に奨励することです。例外を使用して例外的なケース処理を行うと、テストの結果よりもパフォーマンスが大幅に向上します。
2014

1
@ネイトまず第一に、これはすべて例外がどのように実装されるかに依存することを非常に明確に述べました。私は1つの特定の実装をテストしただけでしたが、たくさんあり、Oracleはリリースごとにまったく異なる実装を選択する可能性があります。第二に、例外が例外的である場合、通常は何であるか、もちろん影響は小さいですが、これは明白であるため、明示的に指摘する必要はないと思うので、ここであなたの点をまったく理解できません。そして第三に、例外は使いすぎて悪いので、誰もがそれに同意するので、十分に注意してそれらを使用することは非常に良いことです。
Mecki 2014

4
@Glideスローはクリーンなものではありませんreturn。本体の途中、おそらく操作の途中でもメソッドを残し(これまでのところ50%しか完了していない)、catchブロックが20スタックフレーム上にある場合があります(メソッドにはtryブロックがあり、method1を呼び出します) method2を呼び出し、method3、...を呼び出し、method20で操作の途中で例外がスローされます。スタックは20フレーム上に巻き戻す必要があり、すべての未完了の操作は元に戻す必要があり(操作の半分は行わないでください)、CPUレジスタはクリーンな状態である必要があります。これはすべて時間を消費します。
Mecki

255

参考までに、私はメッキーが行った実験を拡張しました:

method1 took 1733 ms, result was 2
method2 took 1248 ms, result was 2
method3 took 83997 ms, result was 2
method4 took 1692 ms, result was 2
method5 took 60946 ms, result was 2
method6 took 25746 ms, result was 2

最初の3つはMeckiのものと同じです(私のラップトップは明らかに遅いです)。

method4はmethod3と同じですが、を作成するのnew Integer(1)ではなくを作成しますthrow new Exception()

method5は、スローnew Exception()せずにを作成することを除いて、method3に似ています。

method6はmethod3に似ていますが、新しい例外を作成するのではなく、事前に作成された例外(インスタンス変数)をスローする点が異なります。

Javaでは、例外のスローにかかる費用の多くは、スタックトレースの収集に費やされた時間であり、例外オブジェクトが作成されたときに発生します。例外をスローする実際のコストは大きいですが、例外を作成するコストよりもかなり低くなります。


48
+1あなたの答えは、コアの問題に対処します。つまり、を巻き戻してスタックをトレースするのにかかる時間です。次に、エラーがスローされます。これを最終的な回答として選択しました。
エンジニア

8
いいね。〜70%が例外を作成し、〜30%が例外をスローします。良い情報。
chaqke

1
@バジル-あなたは上記の数字からそれを理解することができるはずです。
Hot Licks、

1
これは実装固有である場合があります。これらのベンチマークに使用されたJavaのバージョンは何ですか?
するThorbjörnRavnアンデルセン

3
標準コードでは、例外の作成とスローがまれに(実行時に)発生することに注意してください。そうでない場合は、実行時の状態が非常に悪いか、設計自体に問題があります。どちらの場合でも、パフォーマンスは問題ではありません...
Jean-BaptisteYunès17年

70

AlekseyShipilëvは非常に徹底的な分析を行い、さまざまな条件の組み合わせの下でJava例外をベンチマークしました。

  • 新しく作成された例外と事前に作成された例外
  • スタックトレースの有効化と無効化
  • 要求されたスタックトレースと要求されなかったスタックトレース
  • トップレベルでキャッチされるvsすべてのレベルで再スローされるvsすべてのレベルでチェーン/ラップされる
  • さまざまなレベルのJava呼び出しスタックの深さ
  • インライン最適化なし、極端なインラインあり、デフォルト設定
  • ユーザー定義フィールドの読み取りと読み取りなし

また、さまざまなレベルのエラー頻度でエラーコードをチェックするパフォーマンスと比較します。

結論(彼の投稿から逐語的に引用)は次のとおりです。

  1. 本当に例外的な例外は美しく機能します。それらを設計どおりに使用し、通常のコードで処理される圧倒的に多数の例外的でないケースの中で本当に例外的なケースのみを伝える場合、例外を使用することでパフォーマンスが向上します。

  2. 例外のパフォーマンスコストには2つの主要な要素があります。例外がインスタンス化れるときのスタックトレースの構築と、例外スロー時のスタックの巻き戻しです。

  3. スタックトレースの構築コストは、例外がインスタンス化れた瞬間のスタックの深さに比例します。地球上の誰が、このスローメソッドが呼び出されるスタックの深さを知っているので、それはすでに悪いですか?スタックトレースの生成をオフにしたり、例外をキャッシュしたりしても、パフォーマンスコストのこの部分のみを取り除くことができます。

  4. スタックの巻き戻しのコストは、コンパイルされたコードで例外ハンドラーを近づけることがどれほど幸運かによって異なります。深い例外ハンドラーの検索を回避するようにコードを注意深く構造化することは、おそらく幸運になるのに役立ちます。

  5. 両方の影響を排除すると、例外のパフォーマンスコストはローカルブランチのコストになります。どんなに美しく聞こえても、例外を通常の制御フローとして使用する必要があるという意味ではありません。その場合は、コンパイラーを最適化するのに翻弄されるからです。例外の頻度によって実際の例外が発生することによる不運なコストが償却される、本当に例外的な場合にのみこれらを使用してください。

  6. 楽観的な経験則は、例外の場合は10 ^ -4の頻度であるようです。もちろん、それは、例外自体の重要度、例外ハンドラで実行される正確なアクションなどに依存します。

結局のところ、例外がスローされない場合はコストがかからないため、例外条件が十分にまれである場合、例外処理はif毎回使用するよりも高速です。完全な投稿は一読の価値があります。


41

残念ながら、私の回答はここに投稿するには長すぎます。したがって、ここで要約し、詳細についてはhttp://www.fuwjax.com/how-slow-are-java-exceptions/参照してください。

ここでの本当の質問は、「「決して失敗しないコード」と比較して「失敗は例外として報告される」のがどれほど遅いか」ではありません。受け入れられた応答はあなたに信じさせるかもしれないので。代わりに、質問は「他の方法で報告された障害と比較して、「例外として報告された障害」はどのくらい遅いのですか?」一般に、障害を報告する他の2つの方法は、センチネル値または結果ラッパーを使用することです。

センチネル値は、成功した場合に1つのクラスを返し、失敗した場合に別のクラスを返す試みです。例外をスローするのではなく、ほとんど例外を返すと考えることができます。これには、成功オブジェクトを含む共有親クラスが必要であり、次に「instanceof」チェックを実行し、成功または失敗の情報を取得するためにいくつかのキャストを実行します。

タイプセーフティのリスクがあるため、Sentinelの値は例外よりも高速ですが、係数は約2倍です。さて、それは多くのように思えるかもしれませんが、その2倍は実装の違いのコストをカバーするだけです。実際には、このページの他の場所にあるサンプルコードのように、失敗する可能性のあるメソッドはいくつかの算術演算子よりもはるかに興味深いので、係数ははるかに低くなります。

一方、ラッパーは型の安全性をまったく犠牲にしません。成功と失敗の情報を1つのクラスにラップします。したがって、「instanceof」の代わりに、成功オブジェクトと失敗オブジェクトの両方に「isSuccess()」とゲッターを提供します。ただし、結果オブジェクトは約2倍遅くなります例外を使用するもます。毎回新しいラッパーオブジェクトを作成する方が、例外をスローするよりもはるかにコストがかかることがわかっています。

その上、例外は、メソッドが失敗する可能性があることを示す方法として提供される言語です。APIから常に(ほとんどの場合)機能することが期待され、失敗を報告することが期待されるメソッドを通知する方法は他にありません。

例外は、番兵よりも安全で、結果オブジェクトよりも速く、どちらよりも驚くべきことではありません。try / catchでif / elseを置き換えることはお勧めしませんが、ビジネスロジックであっても、例外は失敗を報告する正しい方法です。

そうは言っても、私が実行したパフォーマンスに大きな影響を与える最も頻繁な2つの方法は、不要なオブジェクトとネストされたループを作成することです。例外を作成するか、作成しないかの選択がある場合は、例外を作成しないでください。例外を作成するか、常に別のオブジェクトを作成するかを選択できる場合は、例外を作成します。


5
レポートせずに障害をチェックする制御実装と比較して、3つの実装の長期的なパフォーマンスをテストすることにしました。プロセスの失敗率は約4%です。テストの反復により、いずれかの戦略に対してプロセスが10000回呼び出されます。各戦略は1000回テストされ、最後の900回は統計の生成に使用されます。ここに
ナノで

2
楽しみのために、例外テストでfillInStackTraceを無効にしました。ここに現在の時刻があります:コントロール347例外351結果364センチネル355
Fuwjax

Fuwjax、私が何かを見逃していない限り(そして私はあなたのSOの投稿だけを読んでいて、ブログの投稿は読んでいないことを認めます)、上記の2つのコメントがあなたの投稿と矛盾しているようです。ベンチマークでは数値が小さいほど良いと思いますよね?この場合、fillInStackTraceを有効にして例外を生成すると(これはデフォルトで通常の動作です)、説明する他の2つの手法よりもパフォーマンスが低下します。私は何かが足りないのですか、それとも実際にあなたの投稿を反証するためにコメントしましたか?
Felix GV 2014年

@Fuwjax-ここで提示する「ロックアンドハードプレイス」の選択を回避する方法は、「成功」を表すオブジェクトを事前に割り当てることです。通常、一般的な障害の場合にオブジェクトを事前に割り当てることもできます。次に、追加の詳細を返すというまれなケースでのみ、新しいオブジェクトが作成されます。(これは整数の「エラーコード」に相当するOOに加えて、最後のエラーの詳細を取得するための個別の呼び出し-数十年にわたって存在していた手法です。)
ToolmakerSteve

@Fuwjaxでは、例外をスローしても、アカウントでオブジェクトが作成されませんか?その推論が理解できません。例外をスローする場合でも、結果オブジェクトを返す場合でも、オブジェクトを作成しています。その意味で、結果オブジェクトは例外をスローするよりも遅くはありません。
Matthias

20

@Mecki@incarnateの回答を、Javaのスタックトレースを埋めることなく拡張しました。

Java 7以降では、を使用できますThrowable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace)。しかし、Java6については、この質問に対する私の答えをください

// This one will regularly throw one
public void method4(int i) throws NoStackTraceThrowable {
    value = ((value + i) / i) << 1;
    // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
    // an AND operation between two integers. The size of the number plays
    // no role. AND on 32 BIT always ANDs all 32 bits
    if ((i & 0x1) == 1) {
        throw new NoStackTraceThrowable();
    }
}

// This one will regularly throw one
public void method5(int i) throws NoStackTraceRuntimeException {
    value = ((value + i) / i) << 1;
    // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
    // an AND operation between two integers. The size of the number plays
    // no role. AND on 32 BIT always ANDs all 32 bits
    if ((i & 0x1) == 1) {
        throw new NoStackTraceRuntimeException();
    }
}

public static void main(String[] args) {
    int i;
    long l;
    Test t = new Test();

    l = System.currentTimeMillis();
    t.reset();
    for (i = 1; i < 100000000; i++) {
        try {
            t.method4(i);
        } catch (NoStackTraceThrowable e) {
            // Do nothing here, as we will get here
        }
    }
    l = System.currentTimeMillis() - l;
    System.out.println( "method4 took " + l + " ms, result was " + t.getValue() );


    l = System.currentTimeMillis();
    t.reset();
    for (i = 1; i < 100000000; i++) {
        try {
            t.method5(i);
        } catch (RuntimeException e) {
            // Do nothing here, as we will get here
        }
    }
    l = System.currentTimeMillis() - l;
    System.out.println( "method5 took " + l + " ms, result was " + t.getValue() );
}

Java 1.6.0_45での出力、Core i7、8GB RAM:

method1 took 883 ms, result was 2
method2 took 882 ms, result was 2
method3 took 32270 ms, result was 2 // throws Exception
method4 took 8114 ms, result was 2 // throws NoStackTraceThrowable
method5 took 8086 ms, result was 2 // throws NoStackTraceRuntimeException

そのため、例外をスローするメソッドと比較して、値を返すメソッドの方が高速です。私見、成功とエラーの両方のフローに戻り値の型を使用するだけで明確なAPIを設計することはできません。スタックトレースなしで例外をスローするメソッドは、通常の例外より4〜5倍高速です。

編集:NoStackTraceThrowable.java ありがとう@Greg

public class NoStackTraceThrowable extends Throwable { 
    public NoStackTraceThrowable() { 
        super("my special throwable", null, false, false);
    }
}

面白い、ありがとう。ここでは欠落しているクラスの宣言があります:public class NoStackTraceThrowable extends Throwable { public NoStackTraceThrowable() { super("my special throwable", null, false, false); } }
グレッグ

あなたが書いたWith Java 7+, we can useが後で書いたOutput with Java 1.6.0_45,ので、これはJava 6または7の結果ですか?
WBAR

1
Java 7の@WBARでは、引数Throwableを持つコンストラクタを使用するだけですboolean writableStackTrace。しかし、それはJava 6以下には存在しません。これが、Java 6以下のカスタム実装を提供した理由です。したがって、上記のコードはJava 6以下用です。2パラ1行目をよくお読みください。
マニカンタ

@manikanta "私見、成功とエラーの両方のフローに戻り値の型を使用するだけで明確なAPIを設計することはできません。
Hejazzman

@Hejazzman同意する。しかしOptional、Javaには少し遅れがありました。その前にも、成功/エラーフラグを持つラッパーオブジェクトを使用しました。しかし、それは少しハックのようで、私には自然に感じません。
manikanta

8

しばらく前に、(1)Integer.parseInt()を呼び出して例外をキャッチする、または(2)文字列を正規表現と一致させてparseInt()を呼び出す、2つのアプローチを使用して文字列をintに変換する相対的なパフォーマンスをテストするクラスを作成しました試合が成功した場合のみ。私はできる限り効率的な方法で正規表現を使用し(つまり、ループに介入する前にパターンオブジェクトとマッチャーオブジェクトを作成しました)、例外からのスタックトレースを出力または保存しませんでした。

1万個の文字列のリストでは、それらがすべて有効な数値である場合、parseInt()アプローチは正規表現アプローチの4倍の速さでした。しかし、文字列の80%しか有効でない場合、正規表現はparseInt()の2倍の速さでした。そして、20%が有効だった場合、つまり例外がスローされて80%の確率でキャッチされた場合、正規表現はparseInt()の約20倍の速さでした。

正規表現のアプローチが有効な文字列を2回処理することを考えると、その結果には驚きました。1回は一致、もう1回はparseInt()です。しかし、それを補う以上の例外を投げてキャッチする。この種の状況は現実の世界ではそれほど頻繁に発生することはありませんが、発生する場合は、例外をキャッチする手法を使用しないでください。しかし、ユーザー入力などを検証するだけの場合は、必ずparseInt()アプローチを使用してください。


どのJVMを使用しましたか?それでも、sun-jdk 6を使用してもそれでも遅いですか?
Benedikt Waldvogel、2008年

私はそれを掘り起こし、その回答を送信する前にJDK 1.6u10で再度実行しました。それらは私が投稿した結果です。
アランムーア

これはとても便利です!ありがとう。私の通常のユースケースでは、(のようなものを使用してInteger.ParseInt())ユーザー入力を解析する必要があり、ユーザー入力はほとんどの場合正しいことが予想されるため、私のユースケースでは、例外的なヒットが時々発生するようです。
markvgti

8

最初の記事は、コールスタックをトラバースし、スタックトレースを作成する行為を高価な部分として言及していると思います。2番目の記事はそれを述べていませんが、オブジェクト作成の最も高価な部分だと思います。ジョンローズは、例外を高速化するためのさまざまな手法を説明する記事を公開しています。(例外の事前割り当てと再利用、スタックトレースなしの例外など)

しかし、それでも-これは必要な悪、最後の手段に過ぎないと考えるべきだと思います。これを行う理由は、JVMで(まだ)利用できない他の言語の機能をエミュレートするためです。制御フローに例外を使用する習慣をつけるべきではありません。特にパフォーマンス上の理由ではありません!#2であなた自身が言及しているように、この方法でコードの重大なバグをマスクするリスクがあり、新しいプログラマーのために維持することは困難になります。

Javaのマイクロベンチマークは、特にJIT領域に入ると、正しく理解するのが驚くほど難しい(私は聞いた)ので、例外を使用する方が実際の「リターン」よりも速いのではないかと私は本当に疑っています。たとえば、テストで2から5の間にスタックフレームがあると思いますか?ここで、JBossによってデプロイされたJSFコンポーネントによってコードが呼び出されることを想像してください。これで、数ページの長さのスタックトレースが作成される可能性があります。

多分あなたはあなたのテストコードを投稿できますか?


7

これらのトピックが関連しているかどうかはわかりませんが、現在のスレッドのスタックトレースに依存する1つのトリックを実装したかったことがあります。私は完全にそれをあきらめました)。そのため、呼び出しThread.currentThread().getStackTrace()非常に遅いことを発見しました(dumpThreads内部で使用するネイティブメソッドのため)。

したがって、Java Throwableにも対応するネイティブメソッドがありfillInStackTraceます。catch前に説明したキラーブロックが何らかの形でこのメソッドの実行をトリガーすると思います。

しかし、別の話をしましょう...

Scalaでは、いくつかの機能機能がを使用してJVMでコンパイルされます。これにより、次のようにControlThrowable拡張ThrowableおよびオーバーライドされますfillInStackTrace

override def fillInStackTrace(): Throwable = this

だから私は上記のテストを適合させました(サイクル量は10だけ減り、私のマシンは少し遅いです:):

class ControlException extends ControlThrowable

class T {
  var value = 0

  def reset = {
    value = 0
  }

  def method1(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      println("You'll never see this!")
    }
  }

  def method2(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      throw new Exception()
    }
  }

  def method3(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new Exception()
    }
  }

  def method4(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new ControlException()
    }
  }
}

class Main {
  var l = System.currentTimeMillis
  val t = new T
  for (i <- 1 to 10000000)
    t.method1(i)
  l = System.currentTimeMillis - l
  println("method1 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method2(i)
  } catch {
    case _ => println("You'll never see this")
  }
  l = System.currentTimeMillis - l
  println("method2 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method4(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method4 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method3(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method3 took " + l + " ms, result was " + t.value)

}

したがって、結果は次のとおりです。

method1 took 146 ms, result was 2
method2 took 159 ms, result was 2
method4 took 1551 ms, result was 2
method3 took 42492 ms, result was 2

ご覧のとおり、との唯一の違いはmethod3method4異なる種類の例外をスローすることです。Yeap、method4よりまだ遅いmethod1method2、その差ははるかに許容可能です。


6

JVM 1.5でいくつかのパフォーマンステストを行いましたが、例外の使用は少なくとも2倍遅くなりました。平均して、例外を除いて、3倍(3倍)を超える非常に小さなメソッドの実行時間。例外をキャッチしなければならないごく小さなループでは、セルフタイムが2倍に増加しました。

プロダクションコードと同様に、マイクロベンチマークでも同様の数値を確認しました。

例外は、頻繁に呼び出されるものには絶対に使用しないでください。1秒間に数千の例外をスローすると、巨大なボトルネックが発生します。

たとえば、 "Integer.ParseInt(...)"を使用して、非常に大きなテキストファイル内のすべての不正な値を検索することは、非常に悪い考えです。(私はこのユーティリティメソッドが製品コードのパフォーマンスを強制終了するの見てきました)

例外を使用して、ユーザーGUIフォームの悪い値を報告します。おそらく、パフォーマンスの観点からはそれほど悪くありません。

それが良い設計プラクティスであるかどうかにかかわらず、私はルールに従います。エラーが正常であるか予期される場合は、戻り値を使用します。異常な場合は、例外を使用してください。たとえば、ユーザー入力を読み取る場合、不正な値は正常です。エラーコードを使用してください。値を内部ユーティリティ関数に渡す場合、不正な値はコードを呼び出すことによってフィルター処理する必要があります。例外を使用してください。


Integer.valueOf(String)を使用する代わりに、フォームに数値が必要な場合は、正規表現マッチャーの使用を検討する必要があります。パターンをプリコンパイルして再利用できるため、マッチャーを安価に作成できます。ただし、GUIフォームでは、isValid / validate / checkFieldまたは何を持っているかはおそらくより明確です。また、Java 8にはオプションのモナドがあるので、それらの使用を検討してください。(答えは9歳ですが、まだです!:p)
HaakonLøtveit17年

4

JavaとC#での例外パフォーマンスには、まだ多くのことが求められています。

プログラマーは、実際のパフォーマンス上の理由から、「例外はまれにしか発生しないはずである」というルールに従う必要があります。

しかし、コンピューター科学者として、私たちはこの問題のある状態に反抗する必要があります。関数を作成する人は、関数が呼び出される頻度や、成功または失敗の可能性が高いかどうかわからないことがよくあります。発信者だけがこの情報を持っています。例外を回避しようとすると、APIのidomが不明確になり、一部のケースでは例外バージョンがクリーンですが遅くなり、他のケースでは高速だが不格好な戻り値エラーが発生します。 。ライブラリの実装者は2つのバージョンのAPIを作成して維持する必要がある場合があり、呼び出し元は各状況で2つのバージョンのどちらを使用するかを決定する必要があります。

これは一種の混乱です。例外のパフォーマンスが向上した場合は、これらの不格好なイディオムを回避し、例外を構造化エラーの返却機能として使用するために使用できます。

戻り値に近い手法を使用して実装された例外メカニズムを実際に見たいので、パフォーマンスを戻り値に近づけることができます。これは、パフォーマンスに敏感なコードでこれに戻るためです。

以下は、例外のパフォーマンスとエラー戻り値のパフォーマンスを比較するコードサンプルです。

パブリッククラスTestIt {

int value;


public int getValue() {
    return value;
}

public void reset() {
    value = 0;
}

public boolean baseline_null(boolean shouldfail, int recurse_depth) {
    if (recurse_depth <= 0) {
        return shouldfail;
    } else {
        return baseline_null(shouldfail,recurse_depth-1);
    }
}

public boolean retval_error(boolean shouldfail, int recurse_depth) {
    if (recurse_depth <= 0) {
        if (shouldfail) {
            return false;
        } else {
            return true;
        }
    } else {
        boolean nested_error = retval_error(shouldfail,recurse_depth-1);
        if (nested_error) {
            return true;
        } else {
            return false;
        }
    }
}

public void exception_error(boolean shouldfail, int recurse_depth) throws Exception {
    if (recurse_depth <= 0) {
        if (shouldfail) {
            throw new Exception();
        }
    } else {
        exception_error(shouldfail,recurse_depth-1);
    }

}

public static void main(String[] args) {
    int i;
    long l;
    TestIt t = new TestIt();
    int failures;

    int ITERATION_COUNT = 100000000;


    // (0) baseline null workload
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                t.baseline_null(shoulderror,recurse_depth);
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("baseline: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);
        }
    }


    // (1) retval_error
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                if (!t.retval_error(shoulderror,recurse_depth)) {
                    failures++;
                }
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("retval_error: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);
        }
    }

    // (2) exception_error
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                try {
                    t.exception_error(shoulderror,recurse_depth);
                } catch (Exception e) {
                    failures++;
                }
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("exception_error: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);              
        }
    }
}

}

そしてここに結果があります:

baseline: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 683 ms
baseline: recurse_depth 2, exception_freqeuncy 0.25 (0), time elapsed 790 ms
baseline: recurse_depth 2, exception_freqeuncy 0.5 (0), time elapsed 768 ms
baseline: recurse_depth 2, exception_freqeuncy 0.75 (0), time elapsed 749 ms
baseline: recurse_depth 2, exception_freqeuncy 1.0 (0), time elapsed 731 ms
baseline: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 923 ms
baseline: recurse_depth 5, exception_freqeuncy 0.25 (0), time elapsed 971 ms
baseline: recurse_depth 5, exception_freqeuncy 0.5 (0), time elapsed 982 ms
baseline: recurse_depth 5, exception_freqeuncy 0.75 (0), time elapsed 947 ms
baseline: recurse_depth 5, exception_freqeuncy 1.0 (0), time elapsed 937 ms
baseline: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1154 ms
baseline: recurse_depth 8, exception_freqeuncy 0.25 (0), time elapsed 1149 ms
baseline: recurse_depth 8, exception_freqeuncy 0.5 (0), time elapsed 1133 ms
baseline: recurse_depth 8, exception_freqeuncy 0.75 (0), time elapsed 1117 ms
baseline: recurse_depth 8, exception_freqeuncy 1.0 (0), time elapsed 1116 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 742 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.25 (24999999), time elapsed 743 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.5 (49999999), time elapsed 734 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.75 (99999999), time elapsed 723 ms
retval_error: recurse_depth 2, exception_freqeuncy 1.0 (99999999), time elapsed 728 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 920 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.25 (24999999), time elapsed 1121   ms
retval_error: recurse_depth 5, exception_freqeuncy 0.5 (49999999), time elapsed 1037 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.75 (99999999), time elapsed 1141   ms
retval_error: recurse_depth 5, exception_freqeuncy 1.0 (99999999), time elapsed 1130 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1218 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.25 (24999999), time elapsed 1334  ms
retval_error: recurse_depth 8, exception_freqeuncy 0.5 (49999999), time elapsed 1478 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.75 (99999999), time elapsed 1637 ms
retval_error: recurse_depth 8, exception_freqeuncy 1.0 (99999999), time elapsed 1655 ms
exception_error: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 726 ms
exception_error: recurse_depth 2, exception_freqeuncy 0.25 (24999999), time elapsed 17487   ms
exception_error: recurse_depth 2, exception_freqeuncy 0.5 (49999999), time elapsed 33763   ms
exception_error: recurse_depth 2, exception_freqeuncy 0.75 (99999999), time elapsed 67367   ms
exception_error: recurse_depth 2, exception_freqeuncy 1.0 (99999999), time elapsed 66990 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 924 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.25 (24999999), time elapsed 23775  ms
exception_error: recurse_depth 5, exception_freqeuncy 0.5 (49999999), time elapsed 46326 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.75 (99999999), time elapsed 91707 ms
exception_error: recurse_depth 5, exception_freqeuncy 1.0 (99999999), time elapsed 91580 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1144 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.25 (24999999), time elapsed 30440 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.5 (49999999), time elapsed 59116   ms
exception_error: recurse_depth 8, exception_freqeuncy 0.75 (99999999), time elapsed 116678 ms
exception_error: recurse_depth 8, exception_freqeuncy 1.0 (99999999), time elapsed 116477 ms

戻り値を確認して伝達すると、ベースラインとヌルの呼び出しに比べていくつかのコストが追加され、そのコストは呼び出しの深さに比例します。呼び出しチェーンの深さが8の場合、エラー戻り値チェックバージョンは、戻り値をチェックしないベースラインバージョンよりも約27%遅くなりました。

これに対して、例外のパフォーマンスは呼び出し深度の関数ではなく、例外の頻度の関数です。ただし、例外の頻度が増えるにつれて退化ははるかに劇的になります。エラー頻度が25%の場合、コードの実行速度は24倍遅くなりました。エラー頻度が100%の場合、例外バージョンはほぼ100倍遅くなります。

これは、おそらく例外実装で間違ったトレードオフをしていることを私に示唆しています。例外は、コストのかかるstalkウォークを回避するか、コンパイラがサポートする戻り値のチェックにそれらを完全に変換することにより、より速くなる可能性があります。それらが実行されるまで、コードを高速で実行する必要がある場合は回避し続けます。


3

HotSpotは、すべてインライン化されている限り、システムで生成された例外の例外コードを削除できます。ただし、明示的に作成された例外とその他の方法で削除されなかった例外は、スタックトレースの作成に多くの時間を費やします。オーバーライドfillInStackTraceして、これがパフォーマンスにどのように影響するかを確認します。


2

例外のスローが遅くない場合でも、通常のプログラムフローで例外をスローすることは、依然として悪い考えです。このように使用すると、GOTOに似ています...

しかし、それは実際には質問の答えにはなりません。以前のJavaバージョン(<1.4)では、スローされる例外をスローするという「従来の」知恵は真実だったと思います。例外を作成するには、VMがスタックトレース全体を作成する必要があります。その後、VMの速度を上げるために多くの変更が行われましたが、これはおそらく改善された領域の1つです。


1
「通常のプログラムフロー」を定義するとよいでしょう。チェック済みの例外をビジネスプロセスの障害として使用し、未チェックの例外を回復不可能な障害として使用することについては多くのことが書かれているため、ある意味では、ビジネスロジックの障害は通常のフローと考えることができます。
スペンサーコルモス2008年

2
@Spencer K:例外は、名前が示すように、例外的な状況が発見されたことを意味します(ファイルがなくなった、ネットワークが突然閉じた、など)。これは、状況が予想外だったことを意味します。状況が発生すると予想される場合は、例外を使用しません。
Mecki

2
@メッキー:そうです。私は最近これについて誰かと話し合いました...彼らは検証フレームワークを書いていて、検証が失敗した場合に例外を投げていました。これはよくあることなので、これは悪い考えだと思います。メソッドがValidationResultを返すのを確認します。
user38051 2008年

2
制御フローの点では、例外はbreakまたはreturnではなく、またはに類似していgotoます。
Hot Licks 2013

3
プログラミングのパラダイムはたくさんあります。それが何を意味するにせよ、単一の「通常のフロー」はあり得ません。基本的に、例外メカニズムは、現在のフレームをすばやく離れ、特定のポイントまでスタックをほどく方法です。「例外」という言葉は、その「予期しない」性質について何も意味しません。簡単な例:ルーティングの途中で特定の状況が発生した場合、Webアプリケーションから404を「スロー」するのは非常に自然なことです。なぜそのロジックは例外付きで実装されないのですか?アンチパターンとは何ですか?
化身

2

Integer.parseIntを次のメソッドと比較してみましょう。このメソッドは、例外をスローする代わりに、解析できないデータの場合にデフォルト値を返すだけです。

  public static int parseUnsignedInt(String s, int defaultValue) {
    final int strLength = s.length();
    if (strLength == 0)
      return defaultValue;
    int value = 0;
    for (int i=strLength-1; i>=0; i--) {
      int c = s.charAt(i);
      if (c > 47 && c < 58) {
        c -= 48;
        for (int j=strLength-i; j!=1; j--)
          c *= 10;
        value += c;
      } else {
        return defaultValue;
      }
    }
    return value < 0 ? /* übergebener wert > Integer.MAX_VALUE? */ defaultValue : value;
  }

両方のメソッドを「有効な」データに適用する限り、どちらもほぼ同じ速度で動作します(Integer.parseIntがより複雑なデータを処理するように管理されている場合でも)。ただし、無効なデータを解析しようとすると(たとえば、 "abc" 1.000.000回を解析する場合)、パフォーマンスの違いが重要になります。


2

例外のパフォーマンスに関する素晴らしい記事は次のとおりです。

https://shipilev.net/blog/2014/exceptional-performance/

スタックトレースあり、なしでの既存のインスタンス化と再利用など:

Benchmark                            Mode   Samples         Mean   Mean error  Units

dynamicException                     avgt        25     1901.196       14.572  ns/op
dynamicException_NoStack             avgt        25       67.029        0.212  ns/op
dynamicException_NoStack_UsedData    avgt        25       68.952        0.441  ns/op
dynamicException_NoStack_UsedStack   avgt        25      137.329        1.039  ns/op
dynamicException_UsedData            avgt        25     1900.770        9.359  ns/op
dynamicException_UsedStack           avgt        25    20033.658      118.600  ns/op

plain                                avgt        25        1.259        0.002  ns/op
staticException                      avgt        25        1.510        0.001  ns/op
staticException_NoStack              avgt        25        1.514        0.003  ns/op
staticException_NoStack_UsedData     avgt        25        4.185        0.015  ns/op
staticException_NoStack_UsedStack    avgt        25       19.110        0.051  ns/op
staticException_UsedData             avgt        25        4.159        0.007  ns/op
staticException_UsedStack            avgt        25       25.144        0.186  ns/op

スタックトレースの深さに応じて:

Benchmark        Mode   Samples         Mean   Mean error  Units

exception_0000   avgt        25     1959.068       30.783  ns/op
exception_0001   avgt        25     1945.958       12.104  ns/op
exception_0002   avgt        25     2063.575       47.708  ns/op
exception_0004   avgt        25     2211.882       29.417  ns/op
exception_0008   avgt        25     2472.729       57.336  ns/op
exception_0016   avgt        25     2950.847       29.863  ns/op
exception_0032   avgt        25     4416.548       50.340  ns/op
exception_0064   avgt        25     6845.140       40.114  ns/op
exception_0128   avgt        25    11774.758       54.299  ns/op
exception_0256   avgt        25    21617.526      101.379  ns/op
exception_0512   avgt        25    42780.434      144.594  ns/op
exception_1024   avgt        25    82839.358      291.434  ns/op

その他の詳細(JITのx64アセンブラーを含む)については、元のブログ投稿を参照してください。

つまり、例外(xD)のためにHibernate / Spring / etc-EE-shitが遅くなり、例外から離れたアプリ制御フローを書き換えます(continure/に置き換えて、メソッド呼び出しからCのようにフラグbreakを返すboolean)と、アプリケーションのパフォーマンスが10倍から100倍向上します。 、あなたがそれらを投げる頻度に応じて)))


0

上記の@Meckiの答えを変更して、method1がブール値と呼び出し側メソッドのチェックを返すようにしました。これは、単にExceptionを何にも置き換えられないためです。2回の実行後、method1は依然として最速か、method2と同じくらい高速でした。

これがコードのスナップショットです。

// Calculates without exception
public boolean method1(int i) {
    value = ((value + i) / i) << 1;
    // Will never be true
    return ((i & 0xFFFFFFF) == 1000000000);

}
....
   for (i = 1; i < 100000000; i++) {
            if (t.method1(i)) {
                System.out.println("Will never be true!");
            }
    }

と結果:

実行1

method1 took 841 ms, result was 2
method2 took 841 ms, result was 2
method3 took 85058 ms, result was 2

実行2

method1 took 821 ms, result was 2
method2 took 838 ms, result was 2
method3 took 85929 ms, result was 2

0

例外が処理するために意図され、実行時に予期しない条件をのみ。

コンパイル時に実行できる単純な検証の代わりに例外を使用すると、実行時まで検証が遅れます。これにより、プログラムの効率が低下します。

単純なif..else検証を使用する代わりに例外をスローすると、コードの記述と維持が複雑になります。


-3

例外の速度とプログラムによるデータのチェックについての私の意見。

多くのクラスには文字列から値へのコンバーター(スキャナー/パーサー)があり、尊敬されている有名なライブラリーもありました;)

通常形をしています

class Example {
public static Example Parse(String input) throws AnyRuntimeParsigException
...
}

例外名は単なる例であり、通常はチェックされていません(実行時)ので、スロー宣言は私の写真にすぎません

2番目の形式が存在することもあります。

public static Example Parse(String input, Example defaultValue)

投げない

2番目のコードが利用できない場合(またはプログラマーが読むドキュメントが少なすぎて最初にしか使用しない場合)は、そのようなコードを正規表現で記述します。正規表現はクールで、政治的に正しいなどです。

Xxxxx.regex(".....pattern", src);
if(ImTotallySure)
{
  Example v = Example.Parse(src);
}

このコードを使用すると、プログラマは例外のコストを負担しません。しかし、正規表現の非常に高いコストが常に存在するのに対し、例外の小さなコストが発生する場合があります。

私はそのような状況でほとんどいつも使用します

try { parse } catch(ParsingException ) // concrete exception from javadoc
{
}

スタックトレースなどを分析することなく、あなたの講義の後はかなりスピードが速いと思います。

例外を恐れないでください


-5

例外が通常のリターンよりも遅いのはなぜですか?

スタックトレースをターミナルに出力せず、ファイルなどに保存する限り、catch-blockは他のコードブロックよりも多くの作業を行いません。だから、「スローmy_cool_error()をスローする」のがなぜそんなに遅いのかは想像できません。

良い質問です。このトピックに関する詳細情報を楽しみにしています!


17
例外は、実際には使用されない場合でも、スタックトレースに関する情報をキャプチャする必要があります。
Jon Skeet、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.