Javaでのinstanceofの使用によるパフォーマンスへの影響


314

私はアプリケーションに取り組んでおり、1つの設計アプローチにはinstanceofオペレーターの非常に重い使用が含まれます。OO設計は一般的にの使用を避けようとすることは知っていますがinstanceof、それは別の話であり、この質問は純粋にパフォーマンスに関連しています。パフォーマンスに影響があるかどうか疑問に思っていましたか?と同じくらい速い==ですか?

たとえば、10個のサブクラスを持つ基本クラスがあります。基本クラスを取る単一の関数で、クラスがサブクラスのインスタンスであるかどうかを確認し、いくつかのルーチンを実行します。

私がそれを解決することを考えた他の方法の1つは、代わりに「type id」整数プリミティブを使用し、ビットマスクを使用してサブクラスのカテゴリを表し、次にサブクラス「type id」のビットマスク比較をカテゴリーを表す定数マスク。

されてinstanceof何とかよりも速いことをJVMによって最適化?Javaにこだわりたいのですが、アプリのパフォーマンスが重要です。以前にこの道を下ったことがある誰かがいくつかのアドバイスを提供できればそれはクールです。あまりにもつまらないか、最適化するために間違っていることに焦点を合わせていますか?


81
ただし、問題のポイントは、OOのベストプラクティスの問題を取り除き、パフォーマンスを調べることでした。
Dave L.

3
@Dave L.通常私は同意しますが、OPは彼がいくつかの一般的な最適化手法を探していると述べており、彼の問題が「instanceof」に関連しているかどうかはわかりません。彼は両方の選択肢をプロファイリングできるように、少なくとも「正しい」デザインについて言及する価値があると思います。
アウトロープログラマ

51
うーん...なぜすべての回答が質問の要点を逃し、最適化について同じ古いKnuthレトリックを提供するのですか?あなたの質問は、instanceofがクラスオブジェクトを==でチェックするよりも大幅に/驚くほど遅いかどうかに関するものですが、そうではないことがわかりました。
gubby

3
instanceofとキャストのパフォーマンスは非常に優れています。私はここでの問題に異なるアプローチの周りのJava7でのいくつかのタイミングを投稿:stackoverflow.com/questions/16320014/...
Wheezil

回答:


268

最新のJVM / JICコンパイラは、instanceof、例外処理、リフレクションなど、従来の「遅い」操作のほとんどのパフォーマンスヒットを取り除きました。

Donald Knuthが書いたように、「小さな効率を忘れるべきです。たとえば、97%の時間です。時期尚早な最適化がすべての悪の根源です。」instanceofのパフォーマンスはおそらく問題にはならないので、それが問題であると確信するまで、エキゾチックな回避策を考え出すのに時間を無駄にしないでください。


13
最新のJVM / JIC ..これらの最適化がどのJavaバージョンからカバーされているか説明していただけますか?
ラビシャ

138
パフォーマンスがトピックであるときにKnuthを引用する人が常にいます...忘れて、Knuthも述べました(同じ記事で)ソフトウェアエンジニアリングで普及する必要があります」、彼のほぼすべての仕事はアルゴリズムの効率性に関するものであり、彼は(特に)より良いパフォーマンスを達成するためにアセンブリでアルゴリズムを書きました。Meh ...
kgadek

4
余談ですがtry { ObjT o = (ObjT)object } catch (e) { no not one of these }、もっと速く遅くなるでしょうか?
peterk

35
「オブジェクト」がObjTのインスタンスである場合、それをキャストすることはinstanceofを実行するよりも少し高速ですが、私のクイックテストで見つかった違いは、10,000,000回の反復で10〜20ミリ秒でした。「オブジェクト」がObjTでない場合、例外のキャッチは3000倍以上遅くなりました-instanceofの場合は10ミリ秒に対して31,000ミリ秒以上。
スティーブ

19
「参照」のないそのような強力な議論は、ただ意見を述べただけなので、まったく役に立たない。
marcorossi 2013

279

アプローチ

私が書いたベンチマークプログラムを異なる実装を評価します:

  1. instanceof 実装(参照として)
  2. 抽象クラスと@Overrideテストメソッドを介して方向付けられたオブジェクト
  3. 独自の型実装を使用する
  4. getClass() == _.class 実装

jmhを使用して、ウォームアップコールを100回、測定を1000回繰り返し、10フォークでベンチマークを実行しました。したがって、各オプションは10 000回測定され、macOS 10.12.4およびJava 1.8を搭載したMacBook Proでベンチマーク全体を実行するには12:18:57かかります。ベンチマークは、各オプションの平均時間を測定します。詳細については、GitHubでの実装を参照してください。

完全を期すために、この回答の以前のバージョンと私のベンチマークがあります

結果

| オペレーション| 操作あたりのナノ秒単位のランタイム| instanceofとの比較|
| ------------ | ------------------------------------ -| ------------------------ |
| INSTANCEOF | 39,598±0,022 ns / op | 100,00%|
| GETCLASS | 39,687±0,021 ns / op | 100,22%|
| タイプ| 46,295±0,026 ns / op | 116,91%|
| OO | 48,078±0,026 ns / op | 121,42%|

tl; dr

Java 1.8 instanceofでは、getClass()非常に近いですが、最速のアプローチです。


58
+0.(9)科学について!

16
+私からの他の0.1:D
トビアス・ライヒ

14
@TobiasReichだから私たちは得た+1.0(9)。:)
Pavel

9
これは意味のあることをまったく測定していないと思います。このコードSystem.currentTimeMillis()は、単一のメソッド呼び出しにすぎない操作での使用を測定します。これにより、精度が低くなります。代わりに、JMHなどのベンチマークフレームワークを使用してください。
Lii

6
または、コールごとではなく、10億回のコール全体のタイミングを実行します。
LegendLength 2016年

74

簡単なテストを行って、instanceOfパフォーマンスが、1文字のみの文字列オブジェクトに対する単純なs.equals()呼び出しとどのように比較されるかを確認しました。

10.000.000ループでは、instanceOfは63〜96ミリ秒、equalsは106〜230ミリ秒

私はjava jvm 6を使用しました。

したがって、私の単純なテストでは、1文字の文字列比較ではなく、instanceOfを実行する方が高速です。

文字列の代わりに整数の.equals()を使用すると、同じ結果が得られました。ただし、== iを使用した場合のみ、instanceOfより20ms高速でした(10000.000ループで)


4
ここにコードを投稿することは可能ですか?それは素晴らしいでしょう!
錬金術師2010

7
instanceOfはポリモーフィック関数のディスパッチとどのように比較されましたか?
Chris

21
instanceofをString.equals()と比較するのはなぜですか?タイプを確認する必要がある場合は、object.getClass()。equals(SomeType.class)を実行する必要があります
marsbear

4
@marsbear equals()はそれをカットしません。あなたが必要isAssignableFrom()です。
David Moles

1
@marsbear正解ですが、それはOPが求めていることについてのより良いテストではありません。
デビッドモールズ2014

20

パフォーマンスへの影響を決定する項目は次のとおりです。

  1. instanceof演算子がtrueを返す可能性があるクラスの数
  2. データの分布-ほとんどのinstanceof操作は最初または2回目の試行で解決されましたか?真の操作を返す可能性が最も高いものを最初に配置する必要があります。
  3. デプロイメント環境。Sun Solaris VMでの実行は、SunのWindows JVMとは大きく異なります。Solarisはデフォルトで「サーバー」モードで実行されますが、Windowsはクライアントモードで実行されます。SolarisでのJIT最適化により、すべてのメソッドアクセスが同じようになります。

4つの異なるディスパッチ方法のマイクロベンチマークを作成しました。Solarisの結果は次のとおりです。数値が小さいほど高速です。

InstanceOf 3156
class== 2925 
OO 3083 
Id 3067 

18

あなたの最後の質問への回答:プロファイラーがあなたにインスタンスの中で途方もない時間を費やしていると言わない限り:はい、あなたはつらいです。

最適化する必要のない何かを最適化することについて考える前に、最も読みやすい方法でアルゴリズムを記述して実行します。jit-compilerがそれ自体を最適化する機会を得るまで、それを実行します。その後、このコードに問題が発生した場合は、プロファイラーを使用して、どこを最大限に活用し、これを最適化するかを伝えます。

高度に最適化されたコンパイラーの場合、ボトルネックに関する推測は完全に間違っている可能性があります。

そして、この答えの真の精神(私は心から信じています):jit-compilerが最適化する機会を得たら、instanceofと==がどのように関連しているかは、絶対にわかりません。

私は忘れました:最初の実行を測定しないでください。


1
しかし、元の投稿者はパフォーマンスがこのアプリケーションにとって重要であると述べたので、その状況の初期に最適化することは不合理ではありません。つまり、GWBasicで3Dゲームを作成せず、最後に、これを最適化してみましょう。最初のステップは、C ++に移植することです。
LegendLength 2016年

適切なライブラリが利用可能であれば、GWBasicは3Dゲームの素晴らしいスタートになるかもしれません。しかしそれは別として(人為的な議論であるため)、OPは最適化として完全な書き直しを求めていません。これは、影響が大きいかどうかさえわからない単一の構造に関するものです(現在のバージョンのコンパイラで同じことを実行するためのより良いパフォーマンスの方法がある場合でも)。私はc2.com/cgi/wiki?ProfileBeforeOptimizingと私の答えの後ろにしっかり立っています。予備最適化はすべての悪の根源です!それはメンテナンスを難しくします-そしてメンテナンスは最適化する価値のある側面です
オラフ・コック

15

同じ質問がありますが、私のようなユースケースの「パフォーマンスメトリック」が見つからなかったので、サンプルコードをいくつか追加しました。私のハードウェアとJava 6&7では、10mlnの反復でのinstanceofとswitchの違いは

for 10 child classes - instanceof: 1200ms vs switch: 470ms
for 5 child classes  - instanceof:  375ms vs switch: 204ms

したがって、instanceofは、特にif-else-ifステートメントの数が非常に多い場合は、実際には遅くなりますが、実際のアプリケーションではその差は無視できます。

import java.util.Date;

public class InstanceOfVsEnum {

    public static int c1, c2, c3, c4, c5, c6, c7, c8, c9, cA;

    public static class Handler {
        public enum Type { Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, TypeA }
        protected Handler(Type type) { this.type = type; }
        public final Type type;

        public static void addHandlerInstanceOf(Handler h) {
            if( h instanceof H1) { c1++; }
            else if( h instanceof H2) { c2++; }
            else if( h instanceof H3) { c3++; }
            else if( h instanceof H4) { c4++; }
            else if( h instanceof H5) { c5++; }
            else if( h instanceof H6) { c6++; }
            else if( h instanceof H7) { c7++; }
            else if( h instanceof H8) { c8++; }
            else if( h instanceof H9) { c9++; }
            else if( h instanceof HA) { cA++; }
        }

        public static void addHandlerSwitch(Handler h) {
            switch( h.type ) {
                case Type1: c1++; break;
                case Type2: c2++; break;
                case Type3: c3++; break;
                case Type4: c4++; break;
                case Type5: c5++; break;
                case Type6: c6++; break;
                case Type7: c7++; break;
                case Type8: c8++; break;
                case Type9: c9++; break;
                case TypeA: cA++; break;
            }
        }
    }

    public static class H1 extends Handler { public H1() { super(Type.Type1); } }
    public static class H2 extends Handler { public H2() { super(Type.Type2); } }
    public static class H3 extends Handler { public H3() { super(Type.Type3); } }
    public static class H4 extends Handler { public H4() { super(Type.Type4); } }
    public static class H5 extends Handler { public H5() { super(Type.Type5); } }
    public static class H6 extends Handler { public H6() { super(Type.Type6); } }
    public static class H7 extends Handler { public H7() { super(Type.Type7); } }
    public static class H8 extends Handler { public H8() { super(Type.Type8); } }
    public static class H9 extends Handler { public H9() { super(Type.Type9); } }
    public static class HA extends Handler { public HA() { super(Type.TypeA); } }

    final static int cCycles = 10000000;

    public static void main(String[] args) {
        H1 h1 = new H1();
        H2 h2 = new H2();
        H3 h3 = new H3();
        H4 h4 = new H4();
        H5 h5 = new H5();
        H6 h6 = new H6();
        H7 h7 = new H7();
        H8 h8 = new H8();
        H9 h9 = new H9();
        HA hA = new HA();

        Date dtStart = new Date();
        for( int i = 0; i < cCycles; i++ ) {
            Handler.addHandlerInstanceOf(h1);
            Handler.addHandlerInstanceOf(h2);
            Handler.addHandlerInstanceOf(h3);
            Handler.addHandlerInstanceOf(h4);
            Handler.addHandlerInstanceOf(h5);
            Handler.addHandlerInstanceOf(h6);
            Handler.addHandlerInstanceOf(h7);
            Handler.addHandlerInstanceOf(h8);
            Handler.addHandlerInstanceOf(h9);
            Handler.addHandlerInstanceOf(hA);
        }
        System.out.println("Instance of - " + (new Date().getTime() - dtStart.getTime()));

        dtStart = new Date();
        for( int i = 0; i < cCycles; i++ ) {
            Handler.addHandlerSwitch(h1);
            Handler.addHandlerSwitch(h2);
            Handler.addHandlerSwitch(h3);
            Handler.addHandlerSwitch(h4);
            Handler.addHandlerSwitch(h5);
            Handler.addHandlerSwitch(h6);
            Handler.addHandlerSwitch(h7);
            Handler.addHandlerSwitch(h8);
            Handler.addHandlerSwitch(h9);
            Handler.addHandlerSwitch(hA);
        }
        System.out.println("Switch of - " + (new Date().getTime() - dtStart.getTime()));
    }
}

Java 6とJava 7のどちらの結果ですか?Java 8でこれを再確認しましたか?ここでより重要なのは、instanceofsの長さをintのcaseステートメントに不可欠なものと比較していることです。私たちは、intスイッチが急速に軽量化することを期待していると思います。
Azeroth2b 2017

1
5年前に何が起こったか正確に思い出せません-Java 6とJava 7の両方で同様の結果が得られたと思います。そのため、結果は1つしかありません(ただし、2行が異なるクラス階層の深さの場合)。 、私はJava 8と比較しようとしませんでした。テストのコード全体が提供されています-これをコピーして貼り付け、必要な環境で確認できます(注-現在、これにはJMHテストを使用します)。
Xtra Coder

9

instanceof 非常に高速で、CPU命令をわずかしか使用しません。

明らかに、クラスXにサブクラスがロードされていない場合(JVMが認識)、次のinstanceofように最適化できます。

     x instanceof X    
==>  x.getClass()==X.class  
==>  x.classID == constant_X_ID

主な費用はただの読み取りです!

Xサブクラスがロードされている場合は、さらにいくつかの読み取りが必要です。それらは同じ場所に配置される可能性が高いため、追加コストも非常に低くなります。

皆さん良いニュースです!


2
最適化できますか最適化されていますか?ソース?

@vaxquis 、JVM実装固有として
RecursiveExceptionException

@itzJanuary ため息あなたがここに私の質問のポイントを逃した:誰もがそのコンパイラが知っていることができ、最適化foo-が、されfoo、実際に現在、Oracleのjavacの/ VMによって最適化-または将来的にそれをやるということだけで可能ですか?また、私は回答者に、ドキュメント、ソースコード、開発ブログなどのバッキングソースがあるかどうかを尋ねましたが、実際に最適化できるか、最適化されているかを文書化していますか?それがなければ、この答えは、コンパイラができるかについてのランダムな考えです。

@vaxquis Hotspot VMについて言及したことはありませんが、その場合、「最適化」されているかどうかはわかりません。
RecursiveExceptionException

1
最近読んだJIT(JVM 8)は、直接呼び出しによって1つまたは2つのタイプの呼び出しサイトを最適化しますが、実際のタイプが3つ以上検出されると、vtableに戻ります。したがって、実行時に2つの具象型が呼び出しサイトを通過するだけで、パフォーマンスが向上します。
simon.watts 2017年

5

Instanceofは非常に高速です。つまり、クラス参照の比較に使用されるバイトコードになります。ループで数百万のインスタンスを試して、自分の目で確かめてください。


5

instanceofは、ほとんどの実際の実装(つまり、instanceofが実際に必要なもの)の単純なequalsよりもコストがかかる可能性が高く、初心者向けのすべての教科書と同様に、一般的なメソッドをオーバーライドするだけでは解決できません。上記のDemianが推奨)。

何故ですか?おそらく何が起こるかと言うと、いくつかのインターフェースがあり、いくつかの機能(たとえば、インターフェースx、y、z)を提供し、操作するオブジェクトがそれらのインターフェースの1つを実装する(またはしない)場合がありますが、直接ではありません。たとえば、私が持っているとしましょう:

wはxを拡張します

Aはwを実装します

BはAを拡張します

CはBを拡張し、yを実装します

DはCを拡張し、zを実装します

Dのインスタンスであるオブジェクトdを処理しているとします。コンピューティング(d instanceof x)は、d.getClass()を取得し、実装するインターフェースをループして、1つが==かどうかを知る必要があります。そうでない場合は、祖先すべてに対して再帰的に再帰します...この場合、そのツリーの幅の広い最初の探索を行う場合、yとzが何も拡張しないと仮定すると、少なくとも8つの比較が得られます...

現実世界の派生ツリーの複雑さはより高くなる可能性があります。場合によっては、JITが事前にdを解決できる場合、JITはそのほとんどを最適化することができます。可能な場合はすべて、xを拡張するもののインスタンスとして解決できます。ただし、現実的には、ほとんどの場合、そのツリー内を通過することになります。

それが問題になる場合は、代わりにハンドラーマップを使用して、オブジェクトの具象クラスを処理を行うクロージャーにリンクすることをお勧めします。直接マッピングを優先して、ツリートラバーサルフェーズを削除します。ただし、C.classのハンドラーを設定した場合、上のオブジェクトdは認識されないことに注意してください。

ここに私の2セントがあります。


5

instanceofは非常に効率的であるため、パフォーマンスが低下することはほとんどありません。ただし、instanceofを多く使用すると、設計上の問題が発生します。

xClass == String.classを使用できる場合、これはより高速です。注:最終クラスにはinstanceofは必要ありません。


1
「最終クラスにinstanceofは必要ない」とはどういう意味ですか?
Pacerier 2012年

最終クラスにサブクラスを含めることはできません。この場合x.getClass() == Class.classと同じですx instanceof Class
Peter Lawrey

かっこいい、xがnullでないとしたら、どちらを選びますか?
Pacerier

いい視点ね。それは私xnullそうであると私が期待するかどうかに依存するでしょう。(またはどちらかはっきりしている方)
Peter Lawrey、2012年

うーん、私はちょうど私たちがうまくとしてjava.lang.class.isAssignableFromを使用することができることを実現した場合、あなたは気づいているのinstanceofキーワード内部でこれらのような関数を使用していますか?
Pacerier 2012年

4

一般に、そのような場合(instanceofがこの基本クラスのサブクラスをチェックしている場合)に「instanceof」演算子が不快に感じる理由は、操作をメソッドに移動して適切なものにオーバーライドするためです。サブクラス。たとえば、次の場合:

if (o instanceof Class1)
   doThis();
else if (o instanceof Class2)
   doThat();
//...

あなたはそれを

o.doEverything();

そして、Class1で "doEverything()"を実装し、 "doThis()"を呼び出し、Class2で "doThat()"を呼び出します。


11
しかし、できない場合もあります。オブジェクトを取り込むインターフェースを実装していて、それがどのタイプであるかを通知する必要がある場合は、instanceofが実際に唯一のオプションです。キャストを試すこともできますが、instanceofの方が一般的にクリーンです。
Herms

4

'instanceof'は実際には+または-のような演算子であり、独自のJVMバイトコード命令を持っていると思います。それは十分速いはずです。

オブジェクトがサブクラスのインスタンスであるかどうかをテストしているスイッチがある場合は、設計をやり直す必要があるかもしれません。サブクラス固有の動作をサブクラス自体にプッシュすることを検討してください。


4

デミアンとパウロは良い点について述べています。ただし、実行するコードの配置は、データの使用方法によって異なります...

私はさまざまな方法で使用できる小さなデータオブジェクトの大ファンです。オーバーライド(ポリモーフィック)アプローチに従う場合、オブジェクトは「一方向」でのみ使用できます。

ここにパターンが入ります...

(ビジターパターンの場合のように)ダブルディスパッチを使用して、各オブジェクトにそれ自体を「呼び出す」ように要求することができます。これにより、オブジェクトのタイプが解決されます。ただし、(再度)可能なすべてのサブタイプを「処理」できるクラスが必要です。

処理したいサブタイプごとに戦略を登録できる戦略パターンを使用することを好みます。次のようなもの。これは完全に型が一致する場合にのみ役立ちますが、拡張可能であるという利点があります。サードパーティの寄稿者は独自の型とハンドラを追加できます。(これは、新しいバンドルを追加できるOSGiなどの動的フレームワークに適しています)

うまくいけば、これが他のいくつかのアイデアを刺激するでしょう...

package com.javadude.sample;

import java.util.HashMap;
import java.util.Map;

public class StrategyExample {
    static class SomeCommonSuperType {}
    static class SubType1 extends SomeCommonSuperType {}
    static class SubType2 extends SomeCommonSuperType {}
    static class SubType3 extends SomeCommonSuperType {}

    static interface Handler<T extends SomeCommonSuperType> {
        Object handle(T object);
    }

    static class HandlerMap {
        private Map<Class<? extends SomeCommonSuperType>, Handler<? extends SomeCommonSuperType>> handlers_ =
            new HashMap<Class<? extends SomeCommonSuperType>, Handler<? extends SomeCommonSuperType>>();
        public <T extends SomeCommonSuperType> void add(Class<T> c, Handler<T> handler) {
            handlers_.put(c, handler);
        }
        @SuppressWarnings("unchecked")
        public <T extends SomeCommonSuperType> Object handle(T o) {
            return ((Handler<T>) handlers_.get(o.getClass())).handle(o);
        }
    }

    public static void main(String[] args) {
        HandlerMap handlerMap = new HandlerMap();

        handlerMap.add(SubType1.class, new Handler<SubType1>() {
            @Override public Object handle(SubType1 object) {
                System.out.println("Handling SubType1");
                return null;
            } });
        handlerMap.add(SubType2.class, new Handler<SubType2>() {
            @Override public Object handle(SubType2 object) {
                System.out.println("Handling SubType2");
                return null;
            } });
        handlerMap.add(SubType3.class, new Handler<SubType3>() {
            @Override public Object handle(SubType3 object) {
                System.out.println("Handling SubType3");
                return null;
            } });

        SubType1 subType1 = new SubType1();
        handlerMap.handle(subType1);
        SubType2 subType2 = new SubType2();
        handlerMap.handle(subType2);
        SubType3 subType3 = new SubType3();
        handlerMap.handle(subType3);
    }
}

4

私はjmh-java-benchmark-archetype:2.21に基づいてパフォーマンステストを作成します。JDKはopenjdkで、バージョンは1.8.0_212です。テストマシンはmac proです。テスト結果は次のとおりです。

Benchmark                Mode  Cnt    Score   Error   Units
MyBenchmark.getClasses  thrpt   30  510.818 ± 4.190  ops/us
MyBenchmark.instanceOf  thrpt   30  503.826 ± 5.546  ops/us

結果は次のことを示しています。getClassはinstanceOfよりも優れており、他のテストとは対照的です。しかし、理由はわかりません。

テストコードは次のとおりです。

public class MyBenchmark {

public static final Object a = new LinkedHashMap<String, String>();

@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean instanceOf() {
    return a instanceof Map;
}

@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean getClasses() {
    return a.getClass() == HashMap.class;
}

public static void main(String[] args) throws RunnerException {
    Options opt =
        new OptionsBuilder().include(MyBenchmark.class.getSimpleName()).warmupIterations(20).measurementIterations(30).forks(1).build();
    new Runner(opt).run();
}
}

私が推測した場合、instanceofが行うことは間違いなくより複雑です。getClass()==チェックは、正確な1:1チェックを実行します。ここで、instanceofは階層をチェックします。つまり、myHashSet instanceof Collectionは通過しますが、myHashSet.getClass()== Collection.classは通過しません。基本的に、これらは同等の操作ではないので、パフォーマンスが異なることにも驚くことはありません。
AMTerp

3

特定のJVMがどのようにインスタンスを実装するかを言うのは難しいですが、ほとんどの場合、オブジェクトは構造体に匹敵し、クラスも同様です。すべてのオブジェクト構造体は、それがインスタンスであるクラス構造体へのポインターを持っています。だから実際には

if (o instanceof java.lang.String)

次のCコードと同じくらい高速かもしれません

if (objectStruct->iAmInstanceOf == &java_lang_String_class)

JITコンパイラーが適切に配置されていて、まともな仕事をしていると想定しています。

これはポインターにアクセスするだけであり、ポインターが指す特定のオフセットでポインターを取得し、これを別のポインターと比較します(これは基本的に32ビットの数値が等しいかどうかをテストするのと同じです)、操作は実際にできると思います非常に速くなる。

必ずしもそうである必要はありませんが、JVMに大きく依存します。ただし、これがコードのボトルネック操作であることが判明した場合、JVMの実装はかなり貧弱であると考えます。JITコンパイラがなく、コードを解釈するだけの場合でも、instanceofテストを事実上すぐに実行できます。


1
oがjava.lang.Stringから継承するかどうかを判断する必要はありませんか?
WW。

1
だから私はそれが「かもしれない」と同じくらい速くなると言った。実際にはループを実行し、最初に問題のクラスに対してiAmInstanceOfをチェックしてから、oの継承ツリーを上向きに移動し、oのすべてのスーパークラスに対してこのチェックを繰り返します(このループを数回実行する必要がある場合があります)試合の場合)
Mecki

3

instanceofのパフォーマンスについて折り返しご連絡いたします。しかし、問題(またはその欠如)を完全に回避する方法は、instanceofを実行する必要があるすべてのサブクラスへの親インターフェースを作成することです。インターフェイスは、instanceofチェックを実行する必要があるサブクラスのすべてのメソッドのスーパーセットになります。メソッドが特定のサブクラスに適用されない場合は、このメソッドのダミー実装を提供するだけです。私が問題を誤解していなければ、これが過去に問題を回避した方法です。


2

InstanceOfは、オブジェクト指向の設計が不十分であることを警告しています。

現在のJVMは、instanceOf自体はパフォーマンスの問題ではないことを意味します。特にコア機能のためにそれを頻繁に使用していることに気付いている場合は、デザインを検討するときです。より良い設計へのリファクタリングのパフォーマンス(および単純性/保守性)の向上は、実際のinstanceOfに費やされた実際のプロセッササイクルを大幅に上回ります。呼び出しにます。

非常に小さな単純化したプログラミングの例を示します。

if (SomeObject instanceOf Integer) {
  [do something]
}
if (SomeObject instanceOf Double) {
  [do something different]
}

貧弱なアーキテクチャの場合、SomeObjectを2つの子クラスの親クラスにして、各子クラスがメソッド(doSomething)をオーバーライドするようにすると、コードは次のようになります。

Someobject.doSomething();

61
私はそれを知っています。それは私が尋ねたものではありませんでした。
Josh、

これは良い点なので、賛成票を投じるかどうかは不明ですが、尋ねられた質問には回答しません...
jklp '12

7
コード例は実際には非常に悪いものだと思います。Doubleクラスを拡張したり、Doubleを他のクラスから派生させることはできません。この例で他のクラスを使用している場合は、問題ありません。
Lena Schimmel、

6
また、SomeObjectの子クラスが値オブジェクトである場合、それらにロジックを配置する必要はありません。たとえば、PieとRoastは、putInOven()およびputInMouth()ロジックの正しい場所ではない可能性があります。
sk。

自炊のパイとローストは素晴らしいですが
binboavetonik

2

最新のJavaバージョンでは、instanceof演算子は単純なメソッド呼び出しとして高速です。これの意味は:

if(a instanceof AnyObject){
}

次のように高速です:

if(a.getType() == XYZ){
}

もう1つは、多数のinstanceofをカスケードする必要がある場合です。次に、getType()を1回だけ呼び出すスイッチの方が高速です。


1

速度が唯一の目的である場合、intクラスの定数を使用してサブクラスを特定すると、ミリ秒単位の時間がかかるようです。

static final int ID_A = 0;
static final int ID_B = 1;
abstract class Base {
  final int id;
  Base(int i) { id = i; }
}
class A extends Base {
 A() { super(ID_A); }
}
class B extends Base {
 B() { super(ID_B); }
}
...
Base obj = ...
switch(obj.id) {
case  ID_A: .... break;
case  ID_B: .... break;
}

ひどいOO設計ですが、パフォーマンス分析でこれがボトルネックになる可能性があることがわかった場合は、おそらくそうです。私のコードでは、ディスパッチコードは合計実行時間の10%を占めています。これにより、全体の速度が1%向上した可能性があります。


0

それが本当にプロジェクトのパフォーマンスの問題であるかどうかを測定/プロファイルする必要があります。可能であれば、再設計をお勧めします。私はあなたがプラットフォームのネイティブ実装(Cで書かれている)を打ち負かすことができないと確信しています。この場合、多重継承も考慮する必要があります。

問題について詳しく説明する必要があります。具体的なタイプのみに関心がある場合は、Map <Class、Object>などの連想ストアを使用できます。


0

Peter Lawreyによる、最終クラスにinstanceofは必要なく、参照の等価性のみを使用できるというメモについては、注意してください。最終クラスを拡張することはできませんが、同じクラスローダーによってロードされることは保証されていません。x.getClass()== SomeFinal.classまたはそのilkを使用するのは、コードのそのセクションでクラスローダーが1つしか機能していないと確信している場合のみです。


4
クラスが別のクラスローダーによってロードされた場合、instanceofも一致しないと思います。
Peter Lawrey、

0

私は列挙型のアプローチも好みますが、抽象基本クラスを使用して、サブクラスにgetType()メソッドを実装させるようにします。

public abstract class Base
{
  protected enum TYPE
  {
    DERIVED_A, DERIVED_B
  }

  public abstract TYPE getType();

  class DerivedA extends Base
  {
    @Override
    public TYPE getType()
    {
      return TYPE.DERIVED_A;
    }
  }

  class DerivedB extends Base
  {
    @Override
    public TYPE getType()
    {
      return TYPE.DERIVED_B;
    }
  }
}

0

このページの一般的なコンセンサスに、「instanceof」は心配するほど高価ではないという反例を提出する価値があると思いました。私は内側のループにいくつかのコードがあることを発見しました

if (!(seq instanceof SingleItem)) {
  seq = seq.head();
}

ここで、SingleItemでhead()を呼び出すと、値が変更されずに返されます。コードの置き換え

seq = seq.head();

文字列からdoubleへの変換のように、ループ内でかなり重いことが起こっているにもかかわらず、269msから169msにスピードアップします。もちろん、インスタンス化演算子自体を削除するよりも、条件分岐を削除した方が速度が向上する可能性があります。しかし、言及する価値があると思いました。


これは、ifそれ自体が原因である可能性があります。truesとfalsesの分布が均一に近い場合、投機的実行は役に立たなくなり、大幅な遅延が発生します。
Dmytro

-4

あなたは間違ったことに焦点を合わせています。同じことをチェックするためのinstanceofと他の方法の違いは、おそらく測定できません。パフォーマンスが重要な場合、Javaはおそらく間違った言語です。主な理由は、VMがガベージコレクションを実行する必要があると判断したときに制御できないためです。これにより、大規模なプログラムで数秒間CPUが100%になる可能性があります(MagicDraw 10はそのために最適です)。すべてのコンピューターを制御できない限り、このプログラムを実行できますが、どのバージョンのJVMになるかを保証することはできません。また、古いバージョンの多くは速度に大きな問題がありました。小さなアプリであればJavaで大丈夫かもしれませんが、常にデータを読み取ったり破棄したりしている場合は、GCがいつ起動するわかります。


7
これは、以前よりも、より最近のJavaガベージコレクションアルゴリズムには当てはまりません。最も単純なアルゴリズムでさえ、使用した直後に破棄するメモリの量は気にせず、若い世代のコレクション全体でどれだけのメモリが保持されるかを気にします。
ビルミシェル

3
素晴らしい。ただし、最新のJVMを使用していて、GCの実行時にコンピューターがまだクロールする。デュアルコアの3GB RAMサーバー。Javaは、パフォーマンスが実際に重要な場合に使用する言語ではありません。
08年

@David:アプリが一定期間利用できなくなったときに、リアルタイムで問題が発生する必要はありません。私が遭遇した楽しいのは、GCの実行時に最初にストリームを閉じず、ネットワークトラフィックの過負荷を処理できなかったためにGCが実行されたときに終了したJavaストリームに接続したJavaアプリです。 GCが実行されるループに入ると、アプリが再開すると、大量のデータをチャーンしようとするため、メモリ不足になり、GCがトリガーされます。Javaは多くのタスクに最適ですが、強力なパフォーマンスが必要です。
11

6
@tloachはアプリのデザインが悪いように聞こえます。「パフォーマンス」は一次元的であるかのように話します。たとえば、非常に大規模なデータセットの迅速でインタラクティブな統計分析と視覚化を提供したり、非常に大規模なトランザクションボリュームを非常に高速に処理したりするなど、多くのJavaアプリを使用してきました。「パフォーマンス」は1つだけのものではありません。メモリを適切に管理せずにGCが独自の方法でアプリケーションを作成できるという事実は、「パフォーマンス」を必要とするものを他の何かで作成する必要があることを意味しません。
David Moles
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.