20

この質問にはすでに答えがあります。

あなたはJavaで例外の投げと処理が高価であることを知っていますか?

私たちのチームでは、実際の例外費用についていくつか議論しました。可能な限り頻繁にそれらを回避する人もいれば、例外を使用することによるパフォーマンスの低下が過大評価されると言う人もいます。

今日、私たちのソフトウェアで次のようなコードが見つかった。

private void doSomething()
{
    try
    {
      doSomethingElse();
    }
    catch(DidNotWorkException e)
    {
       log("A Message");
    }
    goOn();
}
private void doSomethingElse()
{
   if(isSoAndSo())
   {
      throw new DidNotWorkException();
   }
   goOnAgain();
}

これのパフォーマンスはどうですか?

private void doSomething()
{
    doSomethingElse();
    goOn();
}
private void doSomethingElse()
{
   if(isSoAndSo())
   {
      log("A Message");
      return;
   }
   goOnAgain();
}

コードの美的なことなど何も話したくありません。実行時の動作についてだけです。 あなたは実際の経験/測定値を持っていますか?


10 답변


6

私は例外について読むのに煩わされていませんが、あなたのいくつかの修正されたコードで非常に素早いテストをすることで、例外の状況はブール値の場合よりかなり遅いという結論に至ります。

次のような結果が得られました。

Exception:20891ms
Boolean:62ms

このコードから:

public class Test {
    public static void main(String args[]) {
            Test t = new Test();
            t.testException();
            t.testBoolean();
    }
    public void testException() {
            long start = System.currentTimeMillis();
            for(long i = 0; i <= 10000000L; ++i)
                    doSomethingException();
            System.out.println("Exception:" + (System.currentTimeMillis()-start) + "ms");
    }
    public void testBoolean() {
            long start = System.currentTimeMillis();
            for(long i = 0; i <= 10000000L; ++i)
                    doSomething();
            System.out.println("Boolean:" + (System.currentTimeMillis()-start) + "ms");
    }

    private void doSomethingException() {
        try {
          doSomethingElseException();
        } catch(DidNotWorkException e) {
           //Msg
        }
    }
    private void doSomethingElseException() throws DidNotWorkException {
       if(!isSoAndSo()) {
          throw new DidNotWorkException();
       }
    }
    private void doSomething() {
        if(!doSomethingElse())
            ;//Msg
    }
    private boolean doSomethingElse() {
       if(!isSoAndSo())
          return false;
       return true;
    }
    private boolean isSoAndSo() { return false; }
    public class DidNotWorkException extends Exception {}
}

私は愚かに私のコードを十分に読まなかった、そして以前にそれにバグがあった(恥ずかしい)

私の仕様は次のとおりです。

  • 1.5.0_16でコンパイルして実行
  • Sun JVM
  • WinXP SP3
  • Intel Centrino Duo T7200(2.00 GHz、977 MHz)
  • 2.00 GB Ram

私の意見では、例外でないメソッドはdoSomethingElseでログエラーを出さず、呼び出しコードが失敗を処理できるようにブール値を返すことに注意してください。これが失敗する可能性がある複数の領域がある場合は、エラーを内部に記録するか、または例外をスローする必要があります。


  • よろしいですか?プライベートブール値isSoAndSo(){return}でテストしました。;そのような場合、私はあなたの結果に驚いています。 - Kai Huppmann
  • ああ、ご存知のとおり、私はあの血の日のうちの1つを持っています:(例外が2倍の違いを投げられない場合でも、私が出した結果は真実でした。 - Henry B
  • 問題ありませんこの質問に対するあなたのすべての仕事に感謝します! - Kai Huppmann
  • 問題ありません、私は役に立ちましたうれしい。 - Henry B
  • この比較は不公平です。 doSomethingElse()では、まともなコンパイラによって最適化されています。b)それが行われていなくても、現代のCPUの分岐予測機能のために目立つことはありません。通常、この状態はほとんどの場合偽であり、例外的な状況では真です。 CPUの分岐予測では、条件がfalseと評価され、else分岐の命令が読み込まれますが、条件が真(例外的な状況)であると判断された場合は、パイプラインをフラッシュしてthen分岐を読み込む必要がありますこれは、数十CPUサイクルかかることがあります。 - Stefan Majewsky

11

例外は無料ではありません...だから彼らは高価です:-)

その本効果的なJava詳細にこれをカバーしています。

  • 項目39例外を例外的な状況にのみ使用する。
  • 項目40回復可能な条件に対する例外の使用

著者は、例外が原因で、特定のVMとOSの組み合わせで、自分のマシンでのテストケースのコードチューニングが70倍遅くなったことを発見しました。


  • 論理的な誤り - 彼らはただ安くなるかもしれません! - alex
  • いや…彼らはそうではありません:-)(しかし引用を見つけなければならなかったのですが…) - TofuBeer
  • 例外は実に高価です。例外は呼び出しの1%でしか発生しないというベンチマークを作成しましたが、これは多少現実的なケースです。このような状況でも、パフォーマンスペナルティは非常に高く、70倍ではありませんが、それでも非常に高くなります。ソースコードとベンチマーク結果を見るここに。 - Richard Gomes

11

例外をスローする最も遅い部分はスタックトレースを埋める

例外を事前に作成してそれを再利用すると、JITはそれを次のように最適化する可能性があります。機械レベル後藤"

あなたの質問からのコードが本当にきついループに入っていない限り、言われたことのすべては、違いは無視できるでしょう。


  • 例外コンストラクタがsuper(msg、cause、/ * enableSuppression * / false、/ * writableStackTrace * / false)を呼び出す場合は、保存せずに新しい例外を作成できます。 - giampaolo

8

例外に関する遅い部分は、スタックトレースの構築です(のコンストラクタ内)。java.lang.Throwableこれはスタックの深さに依存します。投げること自体は遅くありません。

例外を使用して失敗を知らせます。その場合、パフォーマンスへの影響はごくわずかであり、スタックトレースは障害の原因を特定するのに役立ちます。

制御フローに例外が必要な場合(推奨されません)、プロファイリングによって例外がボトルネックであることが示されている場合は、オーバーライドするExceptionサブクラスを作成します。fillInStackTrace()空の実装です。あるいは(または追加的に)1つの例外のみをインスタンス化し、それをフィールドに格納して常に同じインスタンスをスローします。

以下は、マイクロベンチマークに1つの簡単な方法を追加することによって、スタックトレースなしの例外を示します(欠陥はあるが) の中に受け入れられた答え

public class DidNotWorkException extends Exception {
  public Throwable fillInStackTrace() {
      return this;
  }
}

でJVMを使って実行する-serverモード(Windows 7の場合はバージョン1.6.0_24)の結果:

Exception:99ms
Boolean:12ms

Exception:92ms
Boolean:11ms

違いは実際には無視できるほど小さいです。


  • @Dave Jarvis - もしそれらが2つの別々の実行と代表的な結果であるならば、私は900%の実行時間を「ほとんど違いはない」とは呼ばないでしょう...もしすべてのHello Worldsをコーディングしているならば。 - im so confused
  • それはむしろ違いはほとんどないのではなく10の係数のように見えます。あなたが多くの場所でこれを使用するならば、これはとても痛いでしょう。 - Arne Babenhauserheide

4

これは本質的にJVM固有なので、どんなアドバイスが与えられても盲目的に信頼するべきではありませんが、実際には状況に応じて測定してください。大まかな考えを得るために「100万個の例外をスローしてSystem.currentTimeMillisの違いを出力する」を作成するのは難しくありません。

あなたがリストするコードスニペットのために、それが後でそれを維持することにとって重要である「最小の驚きの道」ではないので、私は彼がここで例外投げを使った理由を徹底的に文書化することを個人的に最初の作者に要求するでしょう。

(複雑な方法で何かをするときはいつも、普通の方法ではなくなぜそのようにしたのかを理解するために読者に不要な仕事をさせる - その仕事はなぜ私の意見では正当化されなければならない理由がなければならないのでそれはそのようにされました)。

例外はとても便利なツールですが、必要なときにだけ使うべきです:)


  • JVM固有の場合は+1。たとえば、Java 1.4のtry-catchブロックには大きなパフォーマンス上の問題がありました(主に後のリリースで修正されました)。 - cletus
  • JVM固有の+1で、実際にテストしています。 Javaの速度は飛躍的に向上しました。 - Kiv

3

実測値はありませんが、例外をスローする方がより高価です。

わかりました、これは.NETフレームワークに関するリンクですが、私は同じことがJavaにもあてはまると思います:

例外&amp; Aパフォーマンス

それは言った、あなたはいつそれらを使用することを躊躇しないでください適切な。それは、フロー制御のためにそれらを使用しないで、例外的な何かが起こったときにそれらを使用することです。あなたが起こることを期待していなかった何か。


  • まったく同じ - 例外的な場合に例外を使用する場合は、問題ありません。 1秒間に数回スローすると、処理が遅くなります。フロー制御用に設計されていないためです。 - ojrac
  • うーん、なぜダウングレード?答えが格下げされた場合、あなたはその理由をやる気にさせるべきです。 - Frederik Gheysels

3

例外が必要な場所(例外的な状況)で例外を使用することに固執すると、メリットはパフォーマンスのペナルティよりはるかに優先されます。コストは実際には実行中のアプリケーションで例外がスローされる頻度の関数なので、私は言うかもしれません。

あなたが挙げた例では、失敗は予期せぬものでも壊滅的なものでもないように見えるので、メソッドは例外を使用するよりむしろ実際に成功ステータスを示すboolを返すべきです。

私が関わってきた数少ないパフォーマンス改善作業では、例外費用はかなり低いものでした。一般的で頻繁に繰り返される操作の複雑さを改善するためには、もっと時間をかけます。


3

ご回答ありがとうございました。

私はついにThorbjørnの提案に従って、自分自身でパフォーマンスを測定する小さなテストプログラムを書きました。年です。結果:いいえ2つのバリエーションの違い(パフォーマンスの点で)。

私はコードの美観や何かについて、すなわち例外の意図が何であるかなどについて尋ねなかったとしても、あなたの大部分はそのトピックについても述べました。しかし、実際には状況は必ずしも明確ではありません。考慮中のケースでは、例外がスローされる状況が例外的なものであるように見えた昔のコードが生まれました。今日はライブラリの使い方が違っていて、異なるアプリケーションの振る舞いや使い方が変わっていて、テストカバレッジはあまり良くありませんが、それでもコードはうまくいきます、少し遅すぎます(それが私がパフォーマンスを要求した理由です!!)。そのような状況では、AからBに変更するのには正当な理由があるはずです。それは、私の考えでは、「それが例外ではなかったのです」とは言えません。

ロギング( "A message")は(他のすべての出来事と比較して)次のとおりです。非常に高価なので、私はこれを取り除きます。

編集

テストコードは、元の記事のものとまったく同じで、メソッドによって呼び出されます。testPerfomance()で囲まれたループ内System.currentTimeMillis()実行時間を取得するために-calls ...しかし:

私は今テストコードを見直し、他のすべて(logステートメント)を回し、以前よりも100倍ループした。そして元の投稿からAの代わりにBを使用するときあなたは4.7百万コールのために4.7秒節約することがわかった。ロンが言ったようにfillStackTraceこれは最も高価な部分(そのためには+1)であり、それを上書きすればほぼ同じ(4.5秒)を節約できます(私のように必要ない場合)。結局のところ、コードは1時間に1000回呼ばれていて、その時間に4.5ミリリットル節約できることがわかっているので、それは私の場合ではまだほぼゼロの違いです。

したがって、上記の私の最初の回答部分は少し誤解を招くようなものでしたが、リファクタリングの費用対効果のバランスについて私が言ったことは、依然として真実です。


  • あなたのテストでは、どのくらいの頻度で例外がスローされますか?毎回ですか? - TofuBeer
  • テスト用のコードを投稿できますか。あなたが使っているJVMやOSと同様に? (ほとんどのJVMベンダが例外ケースを最適化しなくてもよいことを考えると、時間がゼロまたは非常に短いと考えるのは難しいと思います)。 - TofuBeer
  • ああ...そしてこれがあなたが速度の変化を見ていないことをプロダクションコードの中にあるなら... 1)なぜメソッドが呼ばれるたびに例外を投げますか? 2)呼び出される頻度(頻度がそれほど頻繁でない場合は、時間が長くならない) - TofuBeer
  • 5秒以上実行したループでテストした場合(そして100万回程度の繰り返しを実行した場合)を除いて、これは正しくテストされていません。 「stopwatch」のSOを検索してください。ストップウォッチベンチマークの作成についてのアドバイスがあります。 - Lawrence Dol
  • 1時間に1000回は明らかに目立つことはありません。効果的なJavaの本の例を読むと(リンクはグーグルの本のサイトにあるので、それはテキストです)、あなたははるかにパタロジカルなバージョン(人々がプロダクションコードで使用したこと - eek)を見るでしょう。 - TofuBeer

0

私はあなたが少し間違った角度からこれを求めていると思います。例外は合図に使用されるように設計されています例外的なケースとして、そしてプログラムフローメカニズムそのような場合には。それであなたが尋ねるべきである質問は例外です、コードの「論理」をしますか。

例外は通常、意図した用途で十分に機能するように設計されています。それらがボトルネックになるように使われているのであれば、それは何よりも「間違ったこと」を完全に止めさせるためだけに使われているということを示しているのです。設計よりもむしろ問題パフォーマンス問題。

逆に、例外が「正しいことに使用されている」ように見える場合は、それはおそらくそれもまたOKを実行することを意味します。


0

ステートメント1と2を実行しようとしたときに例外が発生しないとしましょう。これら2つのサンプルコードの間にパフォーマンスへの影響はありますか?

いいえの場合、どうすればDoSomething()メソッドはする必要があります笑った作業量(他のメソッドへの呼び出しの負荷など)

1:

try
{
   DoSomething();
}
catch (...)
{
   ...
}

2:

DoSomething();


  • Javaではtryはフリースローです(そして、isn&#39;本当に無料で試してみてください。しかし、私が言ったことを忘れておいてください:-)だから例外がスローされなければスピードのヒットはありません。 。論理の一部は、throwはフロー制御に使用されるべきではないので最適化されないということです - それは異常な場合のためのものではなく一般的なものです。 - TofuBeer

リンクされた質問


関連する質問

最近の質問