Why are interface method invocations slower than concrete invocations?

This is question comes in mind when I finding difference between abstract class and interface. In this post I came to know that interfaces are slow as they required extra indirection. But I am not getting what type of indirection required by the interface and not by the abstract class or concrete class.Please clarify on it. Thanks in advance

There are many performance myths, and some were probably true several years ago, and some might still be true on VMs that don't have a JIT.

The Android documentation (remember that Android don't have a JVM, they have Dalvik VM) used to say that invoking a method on an interfaces was slower than invoking it on a class, so they were contributing to spreading the myth (it's also possible that it was slower on the Dalvik VM before they turned on the JIT). The documentation does now say:

Performance Myths

Previous versions of this document made various misleading claims. We address some of them here.

On devices without a JIT, it is true that invoking methods via a variable with an exact type rather than an interface is slightly more efficient. (So, for example, it was cheaper to invoke methods on a HashMap map than a Map map, even though in both cases the map was a HashMap.) It was not the case that this was 2x slower; the actual difference was more like 6% slower. Furthermore, the JIT makes the two effectively indistinguishable.

Source: Designing for performance on Android

The same thing is probably true for the JIT in the JVM, it would be very odd otherwise.

Performance decrease when using interfaces · Issue #7291 · dotnet , avoid (a) the interface invocation tax and (b) the struct-boxing tax incurred when an It could emit a method where all of the calls to the interface methods are After rechecking my assumptions, I take it back, alas a concrete struct The interface calls are slower, but closer to a non-inlined call (as above)  The invocation of invoke() transfers the method invocation to the ORB and then blocks until the method invocation has been processed. 8.3 STATIC INVOCATION INTERFACE Although the DII is primarily provided for direct use, as shown in Figure 8.2 , it can also be used to connect stubs to the ORB, somewhat like a substitute for a separate SII.

If in doubt, measure it. My results showed no significant difference. When run, the following program produced:

7421714 (abstract)
5840702 (interface)

7621523 (abstract)
5929049 (interface)

But when I switched the places of the two loops:

7887080 (interface)
5573605 (abstract)

7986213 (interface)
5609046 (abstract)

It appears that abstract classes are slightly (~6%) faster, but that should not be noticeable; These are nanoseconds. 7887080 nanoseconds are ~7 milliseconds. That makes it a difference of 0.1 millis per 40k invocations (Java version: 1.6.20)

Here's the code:

public class ClassTest {

    public static void main(String[] args) {
        Random random = new Random();
        List<Foo> foos = new ArrayList<Foo>(40000);
        List<Bar> bars = new ArrayList<Bar>(40000);
        for (int i = 0; i < 40000; i++) {
            foos.add(random.nextBoolean() ? new Foo1Impl() : new Foo2Impl());
            bars.add(random.nextBoolean() ? new Bar1Impl() : new Bar2Impl());
        }

        long start = System.nanoTime();    

        for (Foo foo : foos) {
            foo.foo();
        }

        System.out.println(System.nanoTime() - start);


        start = System.nanoTime();

        for (Bar bar : bars) {
            bar.bar();
        }

        System.out.println(System.nanoTime() - start);    
    }

    abstract static class Foo {
        public abstract int foo();
    }

    static interface Bar {
        int bar();
    }

    static class Foo1Impl extends Foo {
        @Override
        public int foo() {
            int i = 10;
            i++;
            return i;
        }
    }
    static class Foo2Impl extends Foo {
        @Override
        public int foo() {
            int i = 10;
            i++;
            return i;
        }
    }

    static class Bar1Impl implements Bar {
        @Override
        public int bar() {
            int i = 10;
            i++;
            return i;
        }
    }
    static class Bar2Impl implements Bar {
        @Override
        public int bar() {
            int i = 10;
            i++;
            return i;
        }
    }
}

Interface function invocation is horribly slow? · Issue #20116 , Invoking functions on the interface had horrible performance compared to running them on the concrete type: import "testing" type D interface  Fusion of Concurrent Invocations of Exclusive Methods Schematic is executed about 2.8 times slower than C on sequential environment and its speedup on 64 processor environment is comparable to

This is variation on Bozho example. It runs longer and re-uses the same objects so the cache size doesn't matter so much. I also use an array so there is no overhead from the iterator.

public static void main(String[] args) {
    Random random = new Random();
    int testLength = 200 * 1000 * 1000;
    Foo[] foos = new Foo[testLength];
    Bar[] bars = new Bar[testLength];
    Foo1Impl foo1 = new Foo1Impl();
    Foo2Impl foo2 = new Foo2Impl();
    Bar1Impl bar1 = new Bar1Impl();
    Bar2Impl bar2 = new Bar2Impl();
    for (int i = 0; i < testLength; i++) {
        boolean flip = random.nextBoolean();
        foos[i] = flip ? foo1 : foo2;
        bars[i] = flip ? bar1 : bar2;
    }
    long start;
    start = System.nanoTime();
    for (Foo foo : foos) {
        foo.foo();
    }
    System.out.printf("The average abstract method call was %.1f ns%n", (double) (System.nanoTime() - start) / testLength);
    start = System.nanoTime();
    for (Bar bar : bars) {
        bar.bar();
    }
    System.out.printf("The average interface method call was %.1f ns%n", (double) (System.nanoTime() - start) / testLength);
}

prints

The average abstract method call was 4.2 ns
The average interface method call was 4.1 ns

if you swap the order the tests are run you get

The average interface method call was 4.2 ns
The average abstract method call was 4.1 ns

There is more difference in how you run the test than which one you chose.

I got the same result with Java 6 update 26 and OpenJDK 7.


BTW: If you add a loop which only call the same object each time, you get

The direct method call was 2.2 ns

Default interface methods, Default interface methods enable an API author to add methods to an interface The simplest form of this feature is the ability to declare a concrete method in an interface, which is a method with a body. Virtual Modifier vs Sealed Modifier The draft spec suggests a syntax for base interface invocations  If you want to make two invocations on the same method, you need to create two request objects. To invoke a method, you need an object reference to the CORBA object that contains the method. You use the object reference to create a request object, populate the request object with arguments, send the request, wait for the reply, and obtain the result from the request.

An object has a "vtable pointer" of some kind which points to a "vtable" (method pointer table) for its class ("vtable" might be the wrong terminology, but that's not important). The vtable has pointers to all the method implementations; each method has an index which corresponds to a table entry. So, to call a class method, you just look up the corresponding method (using its index) in the vtable. If one class extends another, it just has a longer vtable with more entries; calling a method from the base class still uses the same procedure: that is, look up the method by its index.

However, in calling a method from an interface via an interface reference, there must be some alternative mechanism to find the method implementation pointer. Because a class can implement multiple interfaces, it's not possible for the method to always have the same index in the vtable (for instance). There are various possible ways to resolve this, but no way that is quite as efficient as simple vtable dispatch.

However, as mentioned in the comments, it probably won't make much difference with a modern Java VM implementation.

Essential .Net: The Common Language Runtime, However, because different concrete types may support different interfaces, the To deal with this variability, the CLR adds a second level of indirection when invoking As shown in Figure 6.4, a method invocation through an interface-​based is slightly larger and slower because CORINFO_CLASS STRUCT IA-32 Native  The Android documentation (remember that Android don't have a JVM, they have Dalvik VM) used to say that invoking a method on an interfaces was slower than invoking it on a class, so they were contributing to spreading the myth (it's also possible that it was slower on the Dalvik VM before they turned on the JIT).

I tried to write a test that would quantify all of the various ways methods might be invoked. My findings show that it is not whether a method is an interface method or not that matters, but rather the type of the reference through which you are calling it. Calling an interface method through a class reference is much faster (relative to the number of calls) than calling the same method on the same class via an interface reference.

The results for 1,000,000 calls are...

interface method via interface reference: (nanos, millis) 5172161.0, 5.0

interface method via abstract reference: (nanos, millis) 1893732.0, 1.8

interface method via toplevel derived reference: (nanos, millis) 1841659.0, 1.8

Concrete method via concrete class reference: (nanos, millis) 1822885.0, 1.8

Note that the first two lines of the results are calls to the exact same method, but via different references.

And here is the code...

package interfacetest;

/**
 *
 * @author rpbarbat
 */
public class InterfaceTest
{
    static public interface ITest
    {
        public int getFirstValue();
        public int getSecondValue();
    }

    static abstract public class ATest implements ITest
    {
        int first = 0;

        @Override
        public int getFirstValue()
        {
            return first++;
        }
    }

    static public class TestImpl extends ATest
    {
        int second = 0;

        @Override
        public int getSecondValue()
        {
            return second++;
        }
    }

    static public class Test
    {
        int value = 0;

        public int getConcreteValue()
        {
            return value++;
        }
    }

    static int loops = 1000000;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        // Get some various pointers to the test classes
        // To Interface
        ITest iTest = new TestImpl();

        // To abstract base
        ATest aTest = new TestImpl();

        // To impl
        TestImpl testImpl = new TestImpl();

        // To concrete
        Test test = new Test();

        System.out.println("Method call timings - " + loops + " loops");


        StopWatch stopWatch = new StopWatch();

        // Call interface method via interface reference
        stopWatch.start();

        for (int i = 0; i < loops; i++)
        {
            iTest.getFirstValue();
        }

        stopWatch.stop();

        System.out.println("interface method via interface reference: (nanos, millis)" + stopWatch.getElapsedNanos() + ", " + stopWatch.getElapsedMillis());


        // Call interface method via abstract reference
        stopWatch.start();

        for (int i = 0; i < loops; i++)
        {
            aTest.getFirstValue();
        }

        stopWatch.stop();

        System.out.println("interface method via abstract reference: (nanos, millis)" + stopWatch.getElapsedNanos() + ", " + stopWatch.getElapsedMillis());


        // Call derived interface via derived reference
        stopWatch.start();

        for (int i = 0; i < loops; i++)
        {
            testImpl.getSecondValue();
        }

        stopWatch.stop();

        System.out.println("interface via toplevel derived reference: (nanos, millis)" + stopWatch.getElapsedNanos() + ", " + stopWatch.getElapsedMillis());


        // Call concrete method in concrete class
        stopWatch.start();

        for (int i = 0; i < loops; i++)
        {
            test.getConcreteValue();
        }

        stopWatch.stop();

        System.out.println("Concrete method via concrete class reference: (nanos, millis)" + stopWatch.getElapsedNanos() + ", " + stopWatch.getElapsedMillis());
    }
}


package interfacetest;

/**
 *
 * @author rpbarbat
 */
public class StopWatch
{
    private long start;
    private long stop;

    public StopWatch()
    {
        start = 0;
        stop = 0;
    }

    public void start()
    {
        stop = 0;
        start = System.nanoTime();
    }

    public void stop()
    {
        stop = System.nanoTime();
    }

    public float getElapsedNanos()
    {
        return (stop - start);
    }

    public float getElapsedMillis()
    {
        return (stop - start) / 1000;
    }

    public float getElapsedSeconds()
    {
        return (stop - start) / 1000000000;
    }
}

This was using the Oracles JDK 1.6_24. Hope this helps put this question to bed...

Regards,

Rodney Barbati

Professional Java Development with the Spring Framework, The Pointcut interface could be extended to address matchingfields and other joinpoints. any annotations on the method, or the interface the method is invoked on. when an invocation is made, such as the argument values or the call stack. Dynamic pointcuts are slower to evaluate than static pointcuts and allow less  A shared generic interface (class rather than struct) call, is again worse because its an additional interface dispatch to invoke the interface method on the item in the call; so combining this with IEnumerable you'd be looking at x6 the call cost. So while the interface dispatch is only x2, it can add up.

Java 8 Interface Changes – default method and static method, Prior to java 8, interface in java can only have abstract methods. All the Java 8 allows the interfaces to have default and static methods. Java 8 – Abstract classes vs interfaces However this is not entirely true, even though we can now have concrete methods(methods with body) in interfaces just like abstract class, this  In addition to this, there also has to be validation of any arguments you pass through to the method you are invoking. To make argument passing work, reflection APIs take a parameter that is an array of object ’s, one per argument. So if you using reflection to call the method Add(int x, int y),

Static and Default Methods in Interfaces in Java, Default interface methods are an efficient way to deal with this issue. They allow us to return "The car is slowing down." ;. } } Lastly, let's define a typical main class, which creates an instance of Car and calls its methods: ? Once dynamic language resolved the reference to a Method object it can save it and invoke it every time it needs. One of the problems with reflection is that it is always slower than a direct invocation of a method, because JVM needs to check method visibility, receiver and arguments types, and parameters should be collected into an array (that produces “garbage”).

Web Services: European Conference, ECOWS 2004, Erfurt, Germany, , parameters & return values • Method sequencing Concrete services with different interfaces Then, client applications can make a service type request and the WSML translates these generic requests to concrete web services invocations. If the response time of a service is too slow or the service becomes unavailable  SubscribeHelper: New constructor which is called by LocalInvocationHelper if a subscribe request is done as a local invocation. Changes in the subscribe method to differ between local invocations and WS invocations. SubscriptionHome: Modifications to deal with both WS and local subscriptions. Interface Subscription: new method getNotifyCallback()

Comments
  • What is the source that told you "interfaces are slow"?
  • @Mat The Log4J documentation states, "In log4j, logging requests are made to instances of the Logger class. Logger is a class and not an interface. This measurably reduces the cost of method invocation at the cost of some flexibility." To me, this reads as, "in times when extremely aggressive optimization is needed, interfaces can be a hindrance." I don't know how accurate that is (and never having been in that situation, I can't testify to its accuracy), but it is a reputable source.
  • Related: stackoverflow.com/questions/5451775/….
  • Would be genuinely intrigued to see the test that shows a measurable difference after JIT. Log4J has been around a very very long time, can believe that was true 12 years ago before there was even 1.4.2 hotspot. Tough to believe any measurable difference would be there after JIT on a modern JVM.
  • @Sanjay: your first post is about gcj, which is a terrible Java implementation (they did a necessary job, but in pure production quality, it was never really good). Your second link simply states it as if it were a fact and doesn't give any indication on how he came to that conclusion. Chances are he just read it in a 10 year old article himself.
  • So interfaces are twice as slow as direct. Is this after JIT compiling?
  • @Kevin Kostlan: maybe you shouldn’t ignore the digit before the dot. The ratio of 4.2 and 4.1 is not 2
  • I wonder if JVMs use a technique commonly used by C++ to support multiple inheritance. (multiple vtables, pointer thunking etc)
  • @seand, I don't think so because of garbage collection. It's much easier to implement GC when you can be sure that pointers always point to the start of an object. However, it wouldn't be impossible. Of course thunking has a performance penalty too.
  • this is likely all occurring deep in JVM impl code and not GC'd. The JIT, etc. is likely building the vtable on the fly. I haven't tried reading the code but it seems to me like a reasonable way to do it.
  • You missed my point. An interface reference is a pointer to something. In Java, it most likely points at the object itself. In C++, a base-class pointer can point into the middle of a derived-class object.
  • @devouredelysium If the header is at a fixed offset from the fields, it amounts to the same thing.