In Bezug auf die Leistung sprechen:
TL; DR
Verwenden Sie isInstance oder instanceof mit ähnlicher Leistung. isAssignableFrom ist etwas langsamer.
Sortiert nach Leistung:
- isInstance
- Instanz von (+ 0,5%)
- isAssignableFrom (+ 2,7%)
Basierend auf einem Benchmark von 2000 Iterationen unter JAVA 8 Windows x64 mit 20 Aufwärmiterationen.
In der Theorie
Mit einem weichen Bytecode-Viewer können wir jeden Operator in Bytecode übersetzen.
Im Zusammenhang mit:
package foo;
public class Benchmark
{
public static final Object a = new A();
public static final Object b = new B();
...
}
JAVA:
b instanceof A;
Bytecode:
getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A
JAVA:
A.class.isInstance(b);
Bytecode:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z);
JAVA:
A.class.isAssignableFrom(b.getClass());
Bytecode:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Object getClass(()Ljava/lang/Class;);
invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);
Wenn wir messen, wie viele Bytecode-Anweisungen von jedem Operator verwendet werden, können wir erwarten, dass instanceof und isInstance schneller sind als isAssignableFrom . Die tatsächliche Leistung wird jedoch NICHT durch den Bytecode bestimmt, sondern durch den Maschinencode (der plattformabhängig ist). Lassen Sie uns für jeden Operator einen Mikro-Benchmark durchführen.
Der Benchmark
Gutschrift: Wie von @ aleksandr-dubinsky empfohlen und danke an @yura für die Bereitstellung des Basiscodes, hier ein JMH- Benchmark (siehe diese Tuning-Anleitung ):
class A {}
class B extends A {}
public class Benchmark {
public static final Object a = new A();
public static final Object b = new B();
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testInstanceOf()
{
return b instanceof A;
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testIsInstance()
{
return A.class.isInstance(b);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testIsAssignableFrom()
{
return A.class.isAssignableFrom(b.getClass());
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(TestPerf2.class.getSimpleName())
.warmupIterations(20)
.measurementIterations(2000)
.forks(1)
.build();
new Runner(opt).run();
}
}
Hat die folgenden Ergebnisse geliefert (Punktzahl ist eine Anzahl von Operationen in einer Zeiteinheit . Je höher die Punktzahl, desto besser):
Benchmark Mode Cnt Score Error Units
Benchmark.testIsInstance thrpt 2000 373,061 ± 0,115 ops/us
Benchmark.testInstanceOf thrpt 2000 371,047 ± 0,131 ops/us
Benchmark.testIsAssignableFrom thrpt 2000 363,648 ± 0,289 ops/us
Warnung
- Der Benchmark ist JVM- und plattformabhängig. Da es zwischen den einzelnen Vorgängen keine signifikanten Unterschiede gibt, kann es sein, dass auf einer anderen JAVA-Version und / oder Plattformen wie Solaris, Mac oder Linux ein anderes Ergebnis (und möglicherweise eine andere Reihenfolge!) Erzielt wird.
- Der Benchmark vergleicht die Leistung von "Ist B eine Instanz von A", wenn "B A direkt erweitert". Wenn die Klassenhierarchie tiefer und komplexer ist (wie B X erweitert, das Y erweitert, das Z erweitert, das A erweitert), können die Ergebnisse unterschiedlich sein.
- Es wird normalerweise empfohlen, den Code zuerst zu schreiben, indem Sie einen der Operatoren auswählen (den bequemsten), und dann Ihren Code zu profilieren, um zu überprüfen, ob ein Leistungsengpass vorliegt. Vielleicht ist dieser Operator im Kontext Ihres Codes vernachlässigbar, oder vielleicht ...
- In Bezug auf den vorherigen Punkt kann
instanceof
der Kontext Ihres Codes möglicherweise einfacher optimiert werden als isInstance
beispielsweise ...
Nehmen Sie als Beispiel die folgende Schleife:
class A{}
class B extends A{}
A b = new B();
boolean execute(){
return A.class.isAssignableFrom(b.getClass());
// return A.class.isInstance(b);
// return b instanceof A;
}
// Warmup the code
for (int i = 0; i < 100; ++i)
execute();
// Time it
int count = 100000;
final long start = System.nanoTime();
for(int i=0; i<count; i++){
execute();
}
final long elapsed = System.nanoTime() - start;
Dank der JIT wird der Code irgendwann optimiert und wir erhalten:
- Instanz von: 6ms
- isInstance: 12ms
- isAssignableFrom: 15ms
Hinweis
Ursprünglich hat dieser Beitrag einen eigenen Benchmark mit einer for- Schleife in JAVA durchgeführt, was zu unzuverlässigen Ergebnissen führte, da einige Optimierungen wie Just In Time die Schleife beseitigen können. Daher wurde hauptsächlich gemessen, wie lange der JIT-Compiler für die Optimierung der Schleife benötigt hat: Weitere Informationen finden Sie unter Leistungstest unabhängig von der Anzahl der Iterationen
Verwandte Fragen