instanceof vs ClassCastException performance, which is faster?

Say you are writing a for loop for a java server application. This for loop is iterating over something that in practice (i.e. in production) is always of a specific subtype. But in theory it might not be, so being an average java developer, you add some defensive checking.

Now since the for loop will be run at least once for every request your platform receives, and you receive many millions of requests a week (perhaps you’re working at a place like the BBC?), you decide to spend 5 minutes making it “fast enough”.

So, should you keep that  if(!(r instanceof HttpServletRequest)) that you put in, replace it with try { ... } catch(ClassCastException e) { ... }, or remove the check completely?

Just to be clear: this is a really silly question and you should really just not bother spending your brain cycles thinking about it: java is fast and well-optimized over many years, it will be fast enough and many times faster than all your I/O handling and whatnot.

But, you now asked yourself this question and you really really want to know. You google for it and find no good answer. So you write a microbenchmark:

class InstanceOfBenchmark {
    static long globalCounter = 0;
    static long noCheck = 0;
    static long classCast = 0;
    static long instanceOf = 0;
    
    public static void main(final String[] args) {
        final int loops = 10000;
        final int encounterB = 1000;

        final A[] instances = new A[loops];
        for(int i = 0; i < loops; i++) {
            //if(i % encounterB == 0) {
            //    instances[i] = new B();
            //} else {
            instances[i] = new C();
            //}
        }
        
        for(int i = 0; i < 1000; i++) {
            testNoop(instances);
        }

        for (int i = 0; i < 1000; i++) {
            testClassCast(instances);
        }

        for (int i = 0; i < 1000; i++) {
            testInstanceOf(instances);
        }

        System.out.println("globalCounter = " + globalCounter);
        System.out.println(String.format(
                "Time for no-check test: %d", noCheck));
        System.out.println(String.format(
                "Time for ClassCastException test: %d", classCast));
        System.out.println(String.format(
                "Time for instanceof test: %d", instanceOf));
        System.out.println(String.format(
                "instanceof is slower than classCast by %f ms",
                1.0 * (instanceOf - classCast) / 1000 / 1000 / loops));
        System.out.println(String.format(
                "instanceof is slower than no check by %f ms",
                1.0 * (instanceOf - noCheck) / 1000 / 1000 / loops));
    }
    
    static void testNoop(final A[] instances) {
        long start, end;

        start = System.nanoTime();
        for (final A instance : instances) {
            final C c = (C)instance;
            c.otherDoNothing();
        }
        end = System.nanoTime();
        noCheck += end - start;
    }

    static void testClassCast(final A[] instances) {
        long start, end;

        start = System.nanoTime();
        for (final A instance : instances) {
            final C c;
            try {
                c = (C) instance;
                c.otherDoNothing();
            } catch (ClassCastException e) {
                // ignore
            }
        }
        end = System.nanoTime();
        classCast += end - start;
    }

    static void testInstanceOf(final A[] instances) {
        long start, end;

        start = System.nanoTime();
        for (final A instance : instances) {
            final C c;
            if (!(instance instanceof C)) {
                continue;
            }
            c = (C) instance;
            c.otherDoNothing();
        }
        end = System.nanoTime();
        instanceOf += end - start;
    }
    
    static class A {
        public void doNothing() {
            // does nothing
        }
    }

    static class B extends A {
    }

    static class C extends A {

        public void otherDoNothing() {
            // does _almost_ nothing, but not enough to be a no-op
            globalCounter++;
        }
    }
}

Here’s some sample results on my machine:

globalCounter = 30000000
Time for no-check test: 23981000
Time for ClassCastException test: 21082000
Time for instanceof test: 21551000
instanceof is slower than classCast by 0.000047 ms
instanceof is slower than no check by -0.000243 ms

globalCounter = 30000000
Time for no-check test: 27874000
Time for ClassCastException test: 21886000
Time for instanceof test: 35174000
instanceof is slower than classCast by 0.001329 ms
instanceof is slower than no check by 0.000730 ms

globalCounter = 30000000
Time for no-check test: 26983000
Time for ClassCastException test: 22007000
Time for instanceof test: 22568000
instanceof is slower than classCast by 0.000056 ms
instanceof is slower than no check by -0.000442 ms

So for performance it really doesn’t matter. It’s safe to assume that the JVM JITs and Hotspots its way to the most optimal code path in all cases.

If you want to fiddle some more, note that the above test has some code to allow you to vary the success/failure path; just uncomment/comment a few lines and tweak encounterB. The result is the same in all cases: performance is the same.

Now with that settled once and for all, I can keep my instanceof and get back to work!