java optimization nitpick:在投射之前调用instanceof进行检查是否更快投出一些东西并让它抛出异常?

dso*_*len 14 java optimization casting instanceof

在任何人说什么之前我只是出于好奇而问这个问题; 我不打算根据这个答案做任何过早的优化.

我的问题是关于使用反射和铸造的速度.标准的说法是"反思很慢".我的问题是哪个部分确实很慢,为什么; 特别是在比较某事物是否是另一个实例的父母时.

我非常有信心只是将对象的类与另一个Class对象进行比较的速度与任何比较一样快,大概只是直接比较已经存储在Object状态中的单例对象; 但如果一个班级是另一个班级的父母呢?

我通常认为instanceof它与普通班级检查一样快,但今天我想到了它,似乎有些反思必须在"幕后" instanceof才能发挥作用.我在网上查了一下,发现有人说的instanceof很慢; 大概是因为比较物体的父母所需的反射?

这导致了下一个问题,如何只是铸造.如果我把某事物作为一个物体,那我就得不到了ClassCastException.但是如果将对象转换为自身的父对象,则不会发生这种情况.基本上我正在做一个instanceof电话,或逻辑到那个效果,当我在运行时进行演员表明我不是吗?我从来没有听到过任何人暗示过投射物体之前可能会很慢.不可否认,并非所有演员都是提供对象的父级,但很多演员都是父类.然而,从来没有人暗示这可能会很慢.

那是哪个呢.是instanceof真的没有那么慢?两者都instanceof投射到父类有点慢吗?或者有什么理由可以比instanceof电话更快地完成演员表?

Ric*_*gle 19

一如既往地尝试并在您的特定情况下看到,但是:

- 例外是昂贵的,非常明显.

- 使用代码流的异常几乎总是一个坏主意

编辑:好的我感兴趣所以我写了一个快速测试系统

public class Test{

    public Test(){
        B b=new B();
        C c=new C();


        for(int i=0;i<10000;i++){
            testUsingInstanceOf(b);
            testUsingInstanceOf(c);
            testUsingException(b);
            testUsingException(c);
        }
    }

    public static void main(String[] args){

        Test test=new Test();

    }

    public static boolean testUsingInstanceOf(A possiblyB){
        if (possiblyB instanceof B){
            return true;
        }else{
            return false;
        }
    }
    public static boolean testUsingException(A possiblyB){
        try{
            B b=(B)possiblyB;
            return true;
        }catch(Exception e){
            return false;
        }
    }


    private class A{

    }
    private class B extends A{

    }
    private class C extends A{

    }        
}
Run Code Online (Sandbox Code Playgroud)

档案结果:

by InstanceOf: 4.43 ms
by Exception: 79.4 ms
Run Code Online (Sandbox Code Playgroud)

正如我所说,非常昂贵

即使它总是成为B(模拟当你99%确定它的B时你只需要确定它仍然没有更快:

总是B时的档案结果:

by InstanceOf: 4.48 ms
by Exception: 4.51 ms
Run Code Online (Sandbox Code Playgroud)

  • 这个答案可能很旧,因为时间与我的测试没有关系.请参阅下面的答案中的代码,它表明,当抛出异常的时候,try/catch并不比instanceof慢得多33%的时间 (2认同)

Ste*_*n C 8

有一般答案和特定答案.

一般情况

if (/* guard against exception */) {
    /* do something that would throw an exception */
} else {
    /* recover */
}

// versus

try {
   /* do something that would throw an exception */
} catch (TheException ex) {
   /* recover */
}
Run Code Online (Sandbox Code Playgroud)

事实上,创建/抛出/捕获异常是昂贵的.它们可能比进行测试要贵得多.但是,这并不意味着"测试第一"版本总是更快.这是因为在"测试第一"的版本,实际上可能进行的测试:在第一时间if,并在代码第二次抛出异常.

当你考虑到这一点时,很明显,如果(额外)测试的成本足够大并且异常的相对频率足够小,"先测试"实际上会更慢.例如,在:

if (file.exists() && file.isReadable()) {
    is = new FileInputStream(file);
} else {
    System.err.println("missing file");
}
Run Code Online (Sandbox Code Playgroud)

try {
    is = new FileInputStream(file);
} catch (IOException ex) {
    System.err.println("missing file");
}
Run Code Online (Sandbox Code Playgroud)

"测试优先"方法执行2次额外的系统调用,系统调用很昂贵.如果"遗失文件"情况也不寻常......

第二个混淆因素是最新的HotSpot JIT编译器对异常进行了一些重要的优化.特别是,如果JIT编译器可以确定不使用异常对象的状态,它可以将异常create/throw/catch转换为简单的跳转指令.

具体情况 instanceof

在这种情况下,我们很可能比较这两个:

if (o instanceof Foo) {
    Foo f = (Foo) o;
    /* ... */
} 

// versus

try {
    Foo f = (Foo) o;
} catch (ClassCastException ex) {
   /* */
}
Run Code Online (Sandbox Code Playgroud)

这里进行第二次优化.A instanceof后跟a type cast是一种常见的模式.HotSpot JIT编译器通常可以消除由类型转换执行的动态类型检查...因为这会重复刚刚成功的测试.当你考虑到这一点时,"测试第一"版本不能比"异常"版本慢......即使后者被优化为跳转.