Интерпретация производительности отражения Java: почему она на удивление очень быстрая? - PullRequest
7 голосов
/ 14 июля 2009

Я видел другие потоки, которые говорили, что производительность отражения java в 10-100 раз ниже, чем при использовании вызовов без отражения.

Мои тесты в 1.6 показали, что это не так, но я обнаружил некоторые другие интересные вещи, которые мне нужно, чтобы кто-то объяснил мне.

У меня есть объекты, которые реализуют мой интерфейс. Я сделал три вещи: 1) используя ссылку на объект, я привел этот объект к интерфейсу и вызвал метод через интерфейс 2) используя ссылку на реальный объект, вызвал метод напрямую и 3) вызвал метод через отражение. Я видел, что интерфейсный вызов # 1 был самым быстрым, за которым следовало отражение # 3, но я заметил, что прямой вызов метода был самым медленным с хорошим запасом.

Я не понимаю, я бы ожидал, что прямой вызов будет самым быстрым, затем интерфейс, затем отражение будет намного медленнее.

Blah и ComplexClass находятся в пакете, отличном от основного класса, и оба имеют метод doSomething (int x), который реализует интерфейс и просто печатает целое число x.

Вот мои результаты (времена в мс, результаты очень похожи с несколькими испытаниями): Вызов метода напрямую: 107194 вызов метода непосредственно из объекта, приведенного к интерфейсу: 89594 вызов метода через отражение: 90453

Вот мой код:

public class Main
{

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        Blah x = new Blah();
        ComplexClass cc = new ComplexClass();
        test((Object) x, cc);
    }

    public static void test(Object x, ComplexClass cc)
    {
        long start, end;
        long time1, time2, time3 = 0;
        int numToDo = 1000000;
        MyInterface interfaceClass = (MyInterface) x;

        //warming up the cache
        for (int i = 0; i < numToDo; i++)
        {
            cc.doSomething(i); //calls a method directly
        }

        start = System.currentTimeMillis();
        for (int i = 0; i < numToDo; i++)
        {
            cc.doSomething(i); //calls a method directly
        }
        end = System.currentTimeMillis();
        time1 = end - start;

        start = System.currentTimeMillis();
        for (int i = 0; i < numToDo; i++)
        {
            interfaceClass.doSomething(i); //casts an object to an interface then calls the method
        }
        end = System.currentTimeMillis();
        time2 = end - start;


        try
        {
            Class xClass = x.getClass();
            Class[] argTypes =
            {
                int.class
            };
            Method m = xClass.getMethod("doSomething", argTypes);
            Object[] paramList = new Object[1];
            start = System.currentTimeMillis();
            for (int i = 0; i < numToDo; i++)
            {
                paramList[0] = i;
                m.invoke(x, paramList); //calls via reflection
            }
            end = System.currentTimeMillis();
            time3 = end - start;

        } catch (Exception ex)
        {
        }

        System.out.println("calling a method directly: " + time1);
        System.out.println("calling a method directly from an object cast to an interface: " + time2);
        System.out.println("calling a method through reflection: " + time3);
    }

Ответы [ 3 ]

8 голосов
/ 14 июля 2009

Помещение всех тестов в одну и ту же программу является ошибкой микробенчмаркинга - с производительностью Java связан определенный прогрев. Это самый важный недостаток.

Поместите свои тесты в отдельные программы. Затем проведите тесты несколько раз, чтобы вы почувствовали, когда разогрев закончился, и статистическую значимость.

Также у вас есть огромный метод, содержащий ваш внутренний цикл. Hotspot, кажется, лучше справляется с этим, чем раньше, но все равно это плохо.

Вы должны обнаружить, что -server, вызывающий виртуальный метод (даже если он загружен другим загрузчиком классов) в узком цикле, полностью оптимизируется. Поэтому не имеет смысла говорить, насколько быстрый прямой вызов, чем рефлексивный вызов.

4 голосов
/ 14 июля 2009

Во-первых, в последних JDK рефлексия стала намного быстрее. Во-вторых, я ожидаю, что компилятор Hot Spot собирается оптимизировать все эти вызовы примерно до кода. Он может выполнить анализ времени выполнения, чтобы понять, что вы вызываете одну и ту же функцию снова и снова, чтобы оптимизировать отражение (и вызов виртуальной функции). То же самое с примером интерфейса.

0 голосов
/ 24 октября 2012

Мой тест показывает, что если Java может встроить функцию, прямой вызов может быть очень быстрым. В режиме прямой линии вызов в 200-300 раз быстрее отражения вызова. Протестировано на Ubuntu 12.10, Jdk 1.6.35, CPU Xeon E5-2620.

Java становится все умнее и умнее с каждым днем.

import java.lang.reflect.Method;

public class Main 
{
    static class Test
    {
        int i=0;
        public void set(int value){
            this.i = value;
        }
    }

public static void main( String[] args) throws Exception
{
    Test test = new Test();
    int max = 10000000;

    long direct = System.currentTimeMillis();
    for( int i=0; i<max; i++){
        Integer io = new Integer(i*i);
        test.set(io);
    }
    System.out.println("Direct : " + (System.currentTimeMillis() - direct));

    Method method = Test.class.getMethod("set", Integer.TYPE);
    long reflection = System.currentTimeMillis();        
    for( int i=0; i<max; i++){
        Integer io = new Integer(i*i);
        method.invoke(test, io );
    }
    System.out.println("Reflection : " + ( System.currentTimeMillis() - reflection));

}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...