Java System.nanoTime действительно медленно. Возможно ли реализовать высокопроизводительный Java-профилировщик? - PullRequest
5 голосов
/ 19 марта 2010

Я провел тест и обнаружил, что издержки при вызове функции System.nanoTime () на моей машине составляют не менее 500 нс

Похоже, очень сложно иметь высокопроизводительный Java-профилировщик. Предположим, что для корпоративного программного обеспечения функция занимает около 350 секунд и имеет 12 500 000 000 вызовов метода. Следовательно, количество вызовов System.nanoTime (): 12 500 000 000 * 2 = 25 000 000 000 (одна для начальной отметки времени, одна для конечной отметки времени) И накладные расходы System.nanoTime в общей сложности: 500 нс * 25 000 000 000 = 500 * 25 000 с = 12500 000 с.

Примечание: все данные из реального регистра.

Есть ли лучший способ получить метку времени?

Ответы [ 5 ]

19 голосов
/ 20 марта 2010

Я провел 10 лет, работая над коммерческими профилировщиками производительности Java для использования как в разработке, так и в производстве.

Краткий ответ - да, вы правы. Вы не можете это осуществить. И даже если бы вы могли, положить что-то кроме тривиального инструментария в метод, который вызывается так часто, можно:

  • Изменить способ обработки кода JIT, таким образом
  • искажение показателей производительности сложно предсказать (но, как правило, бесполезно с точки зрения настройки производительности).

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

Это нормально, если инструмент медленнее (или, может быть, «нечасто вызванный» будет лучше?). Например, вы можете избавиться от инструментария, например, большого количества JDBC API для выявления проблем с базой данных.

Для фактической настройки производительности реального Java-кода (в отличие от таких вещей, как Java-вызовы, такие как сеть, файловая система, база данных и т. Д.), Инструментарий просто не подходит. Вы получите более понятные результаты, но никто не занимался инструментами линейного уровня для настройки производительности, вероятно, уже 7 лет - по тем же причинам.

Вместо этого коммерческие профилировщики используют технологию «выборки» - они периодически принимают трассировку стека. У JVMTI есть несколько приятных звонков, которые делают его довольно дешевым каждые несколько мс. Затем вы предполагаете, что все время между трассировками стека было потрачено на новый стек (что, очевидно, не соответствует действительности, но статистически оно дает точные результаты в течение не столь тупо короткого периода измерения) - и у вас есть некоторые действенные показатели производительности без сумасшедших накладных расходов или какого-либо эффекта наблюдателя.

5 голосов
/ 19 марта 2010

Практическое предложение: вместо того, чтобы помещать вызов System.nanoTime () внутри метода, поместите его вне цикла, который вызывает этот метод.

Однако здесь есть более глубокий смысл: вы говорите, что у вас есть метод, который вызывается много раз, и что добавление двух вызовов System.nanoTimes () к этому методу делает его невероятно медленным. Исходя из предоставленных вами данных, ваш метод в 35 000 раз быстрее, чем пара вызовов Systme.nanoTime () (12500000 с / 350 с = ~ 35 000).

Это довольно быстрый метод. Это работает меньше, чем Nano. Я не думаю, что вы сможете сделать это быстрее. Единственные приросты производительности, которые ждут вас, это те, которые основаны на уменьшении количества вызовов этого метода (а не на ускорении работы отдельного метода).

Возможно ли, что данные не точны?

2 голосов
/ 19 марта 2010

Какую ОС вы используете? Windows, Linux или Solaris? Один или несколько процессоров?

System.nanoTime () - это минимальные накладные расходы, которые вы получите. Скорость зависит от того, на какой ОС вы работаете:

В Windows он вызывает QueryPerformanceCounter (), в Linux он использует gettimeofday (), а в Solaris - gethrtime ().

Самым быстрым из них, вероятно, является Solaris gethrtime () - он не слышит обычного системного вызова ОС. Несмотря на это, считается, что на 300 МГц UltraSPARC-блоке требуется «несколько сотен наносекунд». Таким образом, 500 нс звучит о правильном диапазоне. Вы можете получить более быстрое время, используя DTrace, но я этим не пользуюсь.

К сожалению, использование инструментов профилирования приводит к накладным расходам. Но вам действительно нужно выполнить вызовы 12.5 миллиардов методов (в соответствии с вашими номерами) для профилирования вашего кода?

1 голос
/ 19 марта 2010

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

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

0 голосов
/ 16 августа 2015

А общая нагрузка System.nanoTime составляет: 500 нс * 25 000 000 000 = 500 * 25 000 с = 12500000 с.

По моей арифметике:

500 nS * 25,000,000,000 =  12500 S.

Потому что:

(500 * 10^-9) * (25 * 10^9) = 500 * 25
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...