Java профилирование: частный получатель недвижимости имеет большое базовое время - PullRequest
3 голосов
/ 27 марта 2009

Я использую TPTP для профилирования медленно работающего Java-кода, и я наткнулся на что-то интересное. Один из моих получателей приватных свойств имеет большое значение базового времени в результатах анализа времени выполнения. Честно говоря, это свойство вызывается много-много раз, но я бы никогда не подумал, что такое свойство займет очень много времени:

public class MyClass{
    private int m_myValue;    
    public int GetMyValue(){
        return m_myValue;
    }
}

Хорошо, так что, очевидно, в классе больше вещей, но, как вы можете видеть, больше ничего не происходит, когда вызывается метод get (просто верните int). Некоторые цифры для вас:

  • Около 30% Звонков в забеге на добытчике (я работаю, чтобы уменьшить это)
  • Около 25% базового времени пробег проходит в этом геттере
  • Среднее базовое время составляет 0,000175 с

Для сравнения, у меня есть другой метод в другом классе, который использует этот геттер:

private boolean FasterMethod(MyClass instance, int value){
    return instance.GetMyValue() > m_localInt - value;
}

Который имеет гораздо более низкое среднее базовое время, равное 0,000018 с (на порядок ниже).

В чем здесь дело? Я предполагаю, что есть что-то, чего я не понимаю или что мне не хватает:

  1. Действительно ли возврат локального примитива занимает больше времени, чем возврат вычисленного значения?
  2. Должен ли я смотреть на показатель, отличный от базового времени?
  3. Эти результаты вводят в заблуждение, и мне нужно рассмотреть какой-нибудь другой инструмент профилирования?

Редактировать 1: Основываясь на некоторых советах, приведенных ниже, я пометил метод как окончательный и повторно провел тест, но получил те же результаты.

Редактировать 2: Я установил демонстрационную версию YourKit для повторного запуска моих тестов производительности, и результаты YourKit выглядят намного ближе к тому, что я ожидал. Я буду продолжать тестировать YourKit и сообщать о том, что я нашел.

Редактировать 3: Переход на YourKit, похоже, решил мою проблему. Я смог использовать YourKit для определения фактических медленных точек в моем коде. Ниже есть несколько отличных комментариев и постов (соответственно проголосовавших), но я принимаю первого человека, предложившего YourKit как «правильный». (Я никоим образом не связан с YourKit / YMMV) * ​​1041 *

Ответы [ 4 ]

6 голосов
/ 27 марта 2009

Если возможно, попробуйте использовать другой профилировщик (Netbeans один работает хорошо). Это может быть трудно сделать в зависимости от того, как настроен ваш код.

Возможно, что, как и многие другие инструменты, другой профилировщик приведет к другой информации.

Действительно ли возврат локального примитива занимает больше времени, чем возврат расчетное значение?

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

Но у вас нет локального значения (локальное значение в методе, а не в классе).

Что именно вы подразумеваете под "местным"?

Должен ли я смотреть на показатель, отличный от базового времени?

Я не использовал инструменты Eclipse, поэтому я не уверен, как он работает ... это может иметь значение, если это трассировщик или профилировщик выборки (оба могут дать разные результаты для подобных вещей).

Эти результаты вводят в заблуждение, и мне нужно рассмотреть некоторые другие инструмент профилирования?

Я бы рассмотрел другой инструмент, просто чтобы посмотреть, будет ли результат таким же.

Редактировать на основе комментариев:

Если это профилировщик выборки, то, что происходит, по существу, что каждые "n-временные единицы" программа выбирается, чтобы увидеть, где находится программа. Если вы вызываете один метод намного больше, чем другой, он будет отображаться как находящийся в методе, который вызывается more (просто более вероятно, что этот метод выполняется).

Профилировщик трассировки добавляет код в вашу программу (процесс, известный как инструментарий), чтобы по существу регистрировать происходящее.

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

Профилировщики выборки быстрее, но менее точны (они только предполагают, как часто выполняется строка кода).

Итак, если Eclipse использует профилировщик выборки, вы можете увидеть, что вы считаете странным поведением. Переход на профилировщик трассировки даст более точные результаты.

Если Eclipse использует профилировщик трассировки, то изменяющиеся профилировщики должны показывать тот же результат (однако, их новый профилировщик может сделать для вас более очевидным, что происходит).

3 голосов
/ 27 марта 2009

Это звучит немного вводящим в заблуждение - возможно, профилировщик удаляет некоторые оптимизации?

Только для ударов попробуйте сделать метод финальным, чтобы его было проще встроить. Это вполне может быть разница между свойством и FasterMethod. При реальном использовании HotSpot встроит даже виртуальные методы, пока они не будут переопределены в первый раз (IIRC).

РЕДАКТИРОВАТЬ: Отвечая на комментарий Брайана: Да, это обычно тот случай, когда что-то окончательное не поможет производительности (хотя это может быть хорошо с точки зрения дизайна :), потому что Hotspot будет нормально работать вне зависимости от того, может ли он быть встроенным или нет, в зависимости от того, переопределен он или нет. Я предполагал, что этот профилировщик, возможно, испортил это.

РЕДАКТИРОВАТЬ: Теперь мне удалось воспроизвести способ, которым HotSpot «отменяет» оптимизацию классов, которые еще не были расширены (или методы, которые не были переопределены). Это было сложнее сделать для виртуальной машины сервера, чем для клиента, но я сделал это:)

public class Test
{
    public static void main(String[] args)
        throws Exception
    {
        final long iterations = 1000000000L;
        Base b = new Base();
        // Warm up Hotspot
        time(b, 1000);

        // Before we load Derived
        time(b, iterations);

        // Load Derived and use it quickly
        // (Just loading is enough to make the client VM
        // undo its optimizations; the server VM needs more effort)
        Base d = (Base) Class.forName("Derived").newInstance();
        time(d, 1);

        // Time it again with Base
        time(b, iterations);
    }

    private static void time(Base b, long iterations)
    {
        long total = 0;
        long start = System.currentTimeMillis();
        for (long i = 0; i < iterations; i++)
        {
            total += b.getValue();
        }
        long end = System.currentTimeMillis();
        System.out.println("Time: " + (end-start));
        System.out.println("Total: " + total);
    }
}

class Base
{
    public int getValue() { return 1; }
}

class Derived extends Base
{
    @Override
    public int getValue() { return 2; }
}
2 голосов
/ 27 марта 2009

Звучит очень странно. Вы не вызываете переопределяющий метод по ошибке, не так ли?

Я бы хотел загрузить демонстрационную версию YourKit . Это тривиально настроить, и оно должно дать представление о том, что действительно происходит. Если и TPTP, и YourKit согласны, то происходит нечто странное (и я знаю, что это не очень помогает!)

0 голосов
/ 27 марта 2009

То, что раньше имело большое значение для производительности методов такого типа (хотя это может быть до в некоторой степени исторически), заключается в том, что размер вызывающего метода может быть проблемой. HotSpot (и серьезные конкуренты) будут рады встроить небольшие методы (некоторые могут подавиться синхронизированными / try-finally). Однако, если вызывающий метод велик, он может и не быть. Это было, в частности, проблемой со старыми версиями HotSpot C1 / client, у которых был действительно плохой алгоритм распределения регистров (теперь он имеет неплохой и быстрый алгоритм).

...