在Java中使用instanceof对性能的影响
我正在开发一个应用程序,其中一种设计方法涉及大量使用在Java中使用instanceof对性能的影响,java,performance,instanceof,Java,Performance,Instanceof,我正在开发一个应用程序,其中一种设计方法涉及大量使用instanceof操作符。虽然我知道OO设计通常试图避免使用instanceof,但这是另一回事,这个问题纯粹与性能有关。我想知道是否有任何性能影响?Is的速度与==一样快 例如,我有一个包含10个子类的基类。在接受基类的单个函数中,我检查该类是否是子类的实例,并执行一些例程 我想解决这个问题的另一种方法是使用“type id”整数原语,使用位掩码来表示子类的类别,然后将子类“type id”与表示类别的常量掩码进行位掩码比较 JVM是否以某
instanceof
操作符。虽然我知道OO设计通常试图避免使用instanceof
,但这是另一回事,这个问题纯粹与性能有关。我想知道是否有任何性能影响?Is的速度与==
一样快
例如,我有一个包含10个子类的基类。在接受基类的单个函数中,我检查该类是否是子类的实例,并执行一些例程
我想解决这个问题的另一种方法是使用“type id”整数原语,使用位掩码来表示子类的类别,然后将子类“type id”与表示类别的常量掩码进行位掩码比较
JVM是否以某种方式优化了
instanceof
,使其速度更快?我想坚持使用Java,但应用程序的性能至关重要。如果有人曾经走过这条路,可以提供一些建议,那就太酷了。我是不是太挑剔了,还是把重点放在了错误的东西上进行优化?现代JVM/JIT编译器已经消除了大多数传统上“缓慢”操作的性能影响,包括instanceof、异常处理、反射等
正如Donald Knuth所写,“我们应该忘记小效率,比如说97%的时间:过早优化是万恶之源。”instanceof的性能可能不会成为问题,所以不要浪费时间想出奇异的解决方法,直到你确定这就是问题所在。一般来说,“instanceof”的原因是什么在这种情况下(instanceof正在检查这个基类的子类),操作符是不受欢迎的,因为您应该做的是将操作移动到一个方法中,并为适当的子类重写它。例如,如果您有:
if (o instanceof Class1)
doThis();
else if (o instanceof Class2)
doThat();
//...
你可以用
o.doEverything();
然后在Class1中调用“doThis()”,在Class2中调用“doThat()”,等等实现“doEverything()”。'instanceof'实际上是一个运算符,像+或-,我相信它有自己的JVM字节码指令。它应该足够快
我不应该说,如果您有一个开关,您正在测试一个对象是否是某个子类的实例,那么您的设计可能需要重新设计。考虑将子类特定的行为向下推到子类本身。< P>实例非常快。它归结为用于类引用比较的字节码。在一个循环中尝试几百万个instanceof,然后自己看看。如果这真的是项目中的一个性能问题,你应该测量/分析它。如果可能的话,我建议重新设计。我敢肯定,您无法打败平台的本机实现(用C编写)。在这种情况下,您还应该考虑多重继承。
你应该告诉更多关于这个问题的信息,如果你只对具体类型感兴趣,也许你可以使用关联存储,例如地图。回答你的最后一个问题:除非探查器告诉你,你在一个瞬间花费了荒谬的时间。f:是的,你在吹毛求疵 在考虑优化一些不需要优化的东西之前:以最可读的方式编写算法并运行它。运行它,直到jit编译器有机会优化它自己。如果您在这段代码中遇到问题,请使用探查器来告诉您从何处获得最大收益并对此进行优化 在编译器高度优化的时代,您对瓶颈的猜测可能是完全错误的 根据这个答案的真实精神(我完全相信):一旦jit编译器有机会对instanceof和==进行优化,我绝对不知道它们之间的关系
我忘了:永远不要测量第一次跑步。你的注意力放错了地方。instanceof和任何其他检查同一事物的方法之间的差异甚至可能无法测量。如果性能至关重要,那么Java可能是错误的语言。主要原因是,您无法控制VM何时决定要去收集垃圾,在一个大型程序中,这会使CPU在几秒钟内达到100%(MagicDraw 10非常适合)。除非你控制着这个程序将要运行的每台计算机,否则你无法保证它将运行在哪个版本的JVM上,而且许多旧版本的JVM都有严重的速度问题。如果它是一个小应用程序,您可能对Java没问题,但是如果您不断地读取和丢弃数据,那么当GC启动时,您会注意到。很难说某个JVM如何实现的实例,但在大多数情况下,对象与结构类似,类也一样,每个对象结构都有一个指向它是其实例的类结构的指针。所以,实际上
if (o instanceof java.lang.String)
可能与下面的C代码一样快
if (objectStruct->iAmInstanceOf == &java_lang_String_class)
假设一个JIT编译器已经就位并且做得很好
考虑到这只是访问一个指针,在指针指向的某个偏移量处获取一个指针,并将其与另一个指针进行比较(这基本上与测试32位数字是否相等相同),我认为该操作实际上可以非常快
不过,这并不一定非得如此,这在很大程度上取决于JVM。但是,如果这将成为代码中的瓶颈操作,我会认为JVM实现相当差。即使没有JIT编译器并且只解释代码的人也应该能够在几乎没有时间的情况下进行instanceof测试。instanceof是对糟糕的面向对象设计的警告 当前的JVM确实意味着实例本身并不是性能问题。如果您发现自己经常使用它,特别是在核心功能方面,那么现在可能是时候看看了
if (SomeObject instanceOf Integer) {
[do something]
}
if (SomeObject instanceOf Double) {
[do something different]
}
Someobject.doSomething();
if(a instanceof AnyObject){
}
if(a.getType() == XYZ){
}
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);
}
}
InstanceOf 3156
class== 2925
OO 3083
Id 3067
x instanceof X
==> x.getClass()==X.class
==> x.classID == constant_X_ID
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;
}
for 10 child classes - instanceof: 1200ms vs switch: 470ms
for 5 child classes - instanceof: 375ms vs switch: 204ms
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()));
}
}
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;
}
}
}
| Operation | Runtime in nanoseconds per operation | Relative to instanceof |
|------------|--------------------------------------|------------------------|
| INSTANCEOF | 39,598 ± 0,022 ns/op | 100,00 % |
| GETCLASS | 39,687 ± 0,021 ns/op | 100,22 % |
| TYPE | 46,295 ± 0,026 ns/op | 116,91 % |
| OO | 48,078 ± 0,026 ns/op | 121,42 % |
if (!(seq instanceof SingleItem)) {
seq = seq.head();
}
seq = seq.head();
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
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();
}
}