web-dev-qa-db-ja.com

どちらが速いか、catchまたはif-else in Java(WRTパフォーマンス)を試してください

どちらが速いですか:

これのどちらか

try {
  n.foo();
} 
catch(NullPointerException ex) {
}

または

if (n != null) n.foo();
36
Rakesh

どちらが速いかという問題ではなく、正確さの問題です。

例外は、まさにそれである状況、例外です。

通常のビジネスロジックの一部としてnnullである可能性がある場合は、if..elseを使用し、そうでない場合はthrowを例外とします。

53
Mitch Wheat
if (n != null) n.foo();

より高速です。

53
Boris Pavlović

Nullポインターを明示的にテストする方が、例外処理を使用するよりもはるかに高速です。

記録として、例外を使用する際のほとんどのほとんどは、例外オブジェクトのインスタンス化で発生します。特にfillInStackTrace()の呼び出しでは、次のことを行う必要があります。

  • 現在のスレッドのスタックについてすべてのスタックフレームを調べ、
  • スタックフレームの詳細をキャプチャするためのデータ構造を作成します。

場合によっては、例外オブジェクトを再利用するか、アプリケーション固有の例外のfillInStackTrace()メソッドをオーバーライドして操作を行わないようにすることで、これを減らすことができます。どちらの場合も、予期しない例外のデバッグに役立つ適切なスタックトレースが使用できなくなるという欠点があります。 (そして、これらはどちらもOPの例には当てはまりません。)

例外のインスタンス化にはコストがかかりますが、例外のスロー、伝播、およびキャッチも必ずしも安価ではありません。


明示的なヌルテストがより良い考えである2番目の理由があります。このことを考慮:

_try {
    doSomething(a.field);
} catch (NullPointerException ex) {
    System.err.println("a.field is null");
}
_

_a.field_式の評価中ではなく、doSomething(...)の呼び出し内でNPEが発生した場合はどうなりますか?確かに、NPEをキャッチしますが、それを誤診して続行しようとします... _a.field_が設定されていないか何かであると誤って想定します。

「予期される」NPEと「予期しない」NPEを区別することは理論的には可能ですが、実際には非常に困難です。はるかに単純で堅牢なアプローチは、期待するnull値を明示的にテストし(たとえば、ifステートメントを使用)、すべてのNPEをバグとして扱うことです。

(これが@Mitchが「例外を例外として扱う」という意味であると確信していますが、説明的な例で物事を詳しく説明するのに役立つと思います...)

35
Stephen C

これに対する答えは、見た目ほど単純ではありません。これは、オブジェクトが実際にnullである回数の割合に依存するためです。これが非常にまれな場合(たとえば、0.1%の確率で)、さらに高速になる可能性があります。これをテストするために、次の結果でベンチマークを実行しました(Java 1.6クライアント):

Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.45 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.44 seconds
Average time of NullExceptionTest: 0.46 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.42 seconds
Average time of NullExceptionTest: 0.52 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.41 seconds
Average time of NullExceptionTest: 1.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.07 seconds
Average time of NullExceptionTest: 7.48 seconds

これは私にはかなり決定的なようです。 NPEは非常に遅いです。 (必要に応じてベンチマークコードを投稿できます)

編集:興味深い発見をしました。サーバーJVMを使用してベンチマークを行うと、結果が大幅に変化します。

Benchmaring with factor 1.0E-4
Average time of NullIfTest: 0.33 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.0010
Average time of NullIfTest: 0.32 seconds
Average time of NullExceptionTest: 0.33 seconds
Benchmaring with factor 0.01
Average time of NullIfTest: 0.31 seconds
Average time of NullExceptionTest: 0.32 seconds
Benchmaring with factor 0.1
Average time of NullIfTest: 0.28 seconds
Average time of NullExceptionTest: 0.30 seconds
Benchmaring with factor 0.9
Average time of NullIfTest: 0.05 seconds
Average time of NullExceptionTest: 0.04 seconds

サーバーVMを使用すると、違いはほとんど目立ちません。それでも:本当に例外でない限り、NullPointerExceptionのキャッチは使用したくありません。

24
Marc

n.foo()がたまたま内部でNPEをスローした場合、長いデバッグセッションがオフになります(さらに悪いことに、アプリが本番環境で失敗します)。ただそれをしないでください。

とにかく、何ナノ秒節約する予定ですか?

8
Enno Shioji

Java Specialist's Newsletter :)を読んでいるのは私だけではないことに気づきました。

セマンティックの違い(NPEは必ずしもnの逆参照によって引き起こされるわけではありませんが、foo()のエラーによってスローされた可能性があります)、および-があるという事実は別として読みやすさの問題(try/catchはifよりも読者を混乱させます)、n != nullの場合はほぼ同じくらい高速である必要があります(if/elseバージョンにはわずかな利点があります) 、ただし、n == null if/elseの方がはるかに高速です。どうして?

  1. n == nullの場合、VMは新しい例外オブジェクトを作成し、そのスタックトレースを入力する必要があります。スタックトレース情報の取得には非常にコストがかかるため、ここでtry/catchバージョンははるかに高価です。
  2. 条件文は命令パイプラインを妨げるため遅いと考える人もいます。明示的なifを避けることで、n != nullのときに安くなると考えています。ただし、VMは逆参照時に暗黙のnullチェックを実行します...つまり、JITがnが非-でなければならないと判断できない限り、 null、if/elseバージョンで可能です。これは、if/elseバージョンとtry/catchバージョンがほぼ同じように実行される必要があることを意味します。しかし...
  3. ... try/catch句は、JITの方法を妨げる可能性がありますinlineメソッド呼び出し。つまり、try/catchバージョンとif/elseを最適化できない可能性があります。
8
gustafc

良い答えのほかに(例外的な場合には例外を使用してください)、基本的にどこでもnullチェックを回避しようとしているようです。 Java 7には、NPEをスローする代わりにn?.foo()が呼び出されたときにnullを返す「nullsafe」演算子があります。これはGroovy言語から借用しています。トレンドもあります。本当に必要な場合(つまり、ライブラリの処理)を除いて、コードでnullを完全に使用しないようにするため。これに関する詳細については、この他の回答を参照してください。 != nullステートメントの回避

3
Bernard

ifコンストラクトの方が高速です。条件は、マシンコード(プロセッサ命令)に簡単に変換できます。

別の方法(try-catch)では、NullPointerExceptionオブジェクトを作成する必要があります。

2
Andreas_D

通常、例外の処理にはコストがかかります。 VM Spec は、いくらかについての洞察を与えるかもしれませんが、上記の場合、if (n != null) n.foo();の方が高速です。

私はミッチウィートに同意しますが、本当の質問は正しさです。

@Mitch Wheat-彼の弁護では、これはかなり不自然な例です。 :)

2
linuxuser27

間違いなく2番目の形式ははるかに高速です。 _try-catch_シナリオでは、何らかの形式のnew Exception()を実行するexceptionをスローします。次に、メソッド呼び出しであるcatchブロックが呼び出され、その中にあるコードを実行する必要があります。あなたはアイデアを得る。

0
fastcodejava

この問題は、ハインツ博士によって最近議論されました。

http://javaspecialists.eu/webinars/recordings/if-else-npe-teaser.mov

0
Radi

他のポスターによって指摘された多くの理由から、最初にif .. then ..elseの方が優れています。

しかし、それは必然的に速くはありません!これは、nullオブジェクトとnullオブジェクトではないオブジェクトの比率に大きく依存します。 nullをテストするのではなく、例外を処理するのにおそらく数十万倍のリソースが必要ですが、nullオブジェクトが100万オブジェクトごとに1回だけ発生する場合、例外オプションはわずかに高速になります。しかし、プログラムを読みにくくし、デバッグを難しくする価値があるほど速くはありません。

0
James Anderson

try-catchブロックは例外スタックトレースを発生させるため、if-elseの方が高速です。 If-Elseブロックが評価を行うために1つの命令を実行していると見なすことができますが、Try-Catchは何千もの命令を実行して、例外が発生したときに例外を発生させます。

0
FearUs