Why are interface method invocations slower than concrete invocations?

后端 未结 6 982
伪装坚强ぢ
伪装坚强ぢ 2020-11-27 15:24

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 in

6条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-11-27 16:01

    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

提交回复
热议问题