この質問にはすでに答えがあります。
あなたは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();
}
コードの美的なことなど何も話したくありません。実行時の動作についてだけです。 あなたは実際の経験/測定値を持っていますか?
私は例外について読むのに煩わされていませんが、あなたのいくつかの修正されたコードで非常に素早いテストをすることで、例外の状況はブール値の場合よりかなり遅いという結論に至ります。
次のような結果が得られました。
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 {}
}
私は愚かに私のコードを十分に読まなかった、そして以前にそれにバグがあった(恥ずかしい)
私の仕様は次のとおりです。
私の意見では、例外でないメソッドはdoSomethingElseでログエラーを出さず、呼び出しコードが失敗を処理できるようにブール値を返すことに注意してください。これが失敗する可能性がある複数の領域がある場合は、エラーを内部に記録するか、または例外をスローする必要があります。
例外は無料ではありません...だから彼らは高価です:-)
その本効果的なJava詳細にこれをカバーしています。
著者は、例外が原因で、特定のVMとOSの組み合わせで、自分のマシンでのテストケースのコードチューニングが70倍遅くなったことを発見しました。
例外をスローする最も遅い部分はスタックトレースを埋める。
例外を事前に作成してそれを再利用すると、JITはそれを次のように最適化する可能性があります。機械レベル後藤"
あなたの質問からのコードが本当にきついループに入っていない限り、言われたことのすべては、違いは無視できるでしょう。
例外に関する遅い部分は、スタックトレースの構築です(のコンストラクタ内)。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
違いは実際には無視できるほど小さいです。
これは本質的にJVM固有なので、どんなアドバイスが与えられても盲目的に信頼するべきではありませんが、実際には状況に応じて測定してください。大まかな考えを得るために「100万個の例外をスローしてSystem.currentTimeMillisの違いを出力する」を作成するのは難しくありません。
あなたがリストするコードスニペットのために、それが後でそれを維持することにとって重要である「最小の驚きの道」ではないので、私は彼がここで例外投げを使った理由を徹底的に文書化することを個人的に最初の作者に要求するでしょう。
(複雑な方法で何かをするときはいつも、普通の方法ではなくなぜそのようにしたのかを理解するために読者に不要な仕事をさせる - その仕事はなぜ私の意見では正当化されなければならない理由がなければならないのでそれはそのようにされました)。
例外はとても便利なツールですが、必要なときにだけ使うべきです:)
実測値はありませんが、例外をスローする方がより高価です。
わかりました、これは.NETフレームワークに関するリンクですが、私は同じことがJavaにもあてはまると思います:
それは言った、あなたはいつそれらを使用することを躊躇しないでください適切な。それは、フロー制御のためにそれらを使用しないで、例外的な何かが起こったときにそれらを使用することです。あなたが起こることを期待していなかった何か。
例外が必要な場所(例外的な状況)で例外を使用することに固執すると、メリットはパフォーマンスのペナルティよりはるかに優先されます。コストは実際には実行中のアプリケーションで例外がスローされる頻度の関数なので、私は言うかもしれません。
あなたが挙げた例では、失敗は予期せぬものでも壊滅的なものでもないように見えるので、メソッドは例外を使用するよりむしろ実際に成功ステータスを示すboolを返すべきです。
私が関わってきた数少ないパフォーマンス改善作業では、例外費用はかなり低いものでした。一般的で頻繁に繰り返される操作の複雑さを改善するためには、もっと時間をかけます。
ご回答ありがとうございました。
私はついに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ミリリットル節約できることがわかっているので、それは私の場合ではまだほぼゼロの違いです。
したがって、上記の私の最初の回答部分は少し誤解を招くようなものでしたが、リファクタリングの費用対効果のバランスについて私が言ったことは、依然として真実です。
私はあなたが少し間違った角度からこれを求めていると思います。例外は合図に使用されるように設計されています例外的なケースとして、そしてプログラムフローメカニズムそのような場合には。それであなたが尋ねるべきである質問は例外です、コードの「論理」をしますか。
例外は通常、意図した用途で十分に機能するように設計されています。それらがボトルネックになるように使われているのであれば、それは何よりも「間違ったこと」を完全に止めさせるためだけに使われているということを示しているのです。設計よりもむしろ問題パフォーマンス問題。
逆に、例外が「正しいことに使用されている」ように見える場合は、それはおそらくそれもまたOKを実行することを意味します。
ステートメント1と2を実行しようとしたときに例外が発生しないとしましょう。これら2つのサンプルコードの間にパフォーマンスへの影響はありますか?
いいえの場合、どうすればDoSomething()
メソッドはする必要があります笑った作業量(他のメソッドへの呼び出しの負荷など)
1:
try
{
DoSomething();
}
catch (...)
{
...
}
2:
DoSomething();