Как мне компенсировать отсутствие «тихой» машины при тестировании моего Java-приложения? - PullRequest
4 голосов
/ 10 апреля 2019

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

Я не хочу часовое время; Я хочу время процессора. Ни одна из статей, кажется, не упоминает этот маленький аспект. В частности, рекомендация использовать «тихую» машину, кажется, размывает то, что измеряется.

Мне не нужно много деталей, я просто хочу знать, что симуляция A работает примерно на 15% быстрее или медленнее, чем симуляция B или C, несмотря на то, что A некоторое время работала сама по себе, а затем я начал B, затем C. И, возможно, я немного поиграл перед тем, как уйти в отставку, что в течение некоторого времени запускало приложение с более высоким приоритетом. Не говорите мне, что в идеале я должен использовать «тихую» машину; мой вопрос конкретно спрашивает, как сделать бенчмаркинг без выделенной машины для этого. Я также не хочу снижать эффективность своих приложений, измеряя, сколько времени они занимают; кажется, что значительные накладные расходы потребуются только тогда, когда требуется много деталей. Я прав?

Я хочу изменить свои приложения так, чтобы при проверке успешности пакетного задания я также мог видеть, сколько времени потребовалось для достижения этих результатов во время процессора. Может ли сравнительный анализ дать мне ответы, которые я ищу? Могу ли я просто использовать бенчмаркинг Java 9 или мне нужно что-то еще?

1 Ответ

4 голосов
/ 10 апреля 2019

Вы можете измерить время ЦП вместо настенных часов вне JVM достаточно легко в большинстве операционных систем. например time java foo.jar в Unix / Linux или даже perf stat java foo.jar в Linux.

Самая большая проблема в этом состоит в том, что у некоторых рабочих нагрузок больше параллелизма, чем у других . Рассмотрим этот простой пример. Это нереально, но математика работает так же для реальных программ, которые чередуются между более параллельными и менее параллельными фазами.

  • версия A является чисто последовательной в течение 9 минут и поддерживает насыщение 8 ядер в течение 1 минуты. Время настенных часов = 10 минут, время процессора = 17 минут

  • версия B является последовательной в течение 1 минуты и поддерживает работу всех 8 ядер в течение 5 минут. Время стены = 6 минут, время процессора = 5 * 8 + 1 = 41 минута

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

Для двух аналогичных реализаций, которые в основном являются последовательными, время ЦП и время стены могут дать вам разумное предположение.

Но современные JVM, такие как HotSpot, используют многопоточную сборку мусора, поэтому, даже если ваш собственный код никогда не запускает несколько потоков, одна версия, которая заставляет GC выполнять больше работы, может использовать больше процессорного времени, но при этом быть быстрее. Впрочем, это может быть редкостью.


Еще один мешающий фактор: конкуренция за пропускную способность памяти и объем кеша будет означать, что для выполнения той же работы потребуется больше процессорного времени, поскольку ваш код будет тратить больше времени на ожидание памяти.

А с HyperThreading или другими архитектурами процессоров SMT (например, Ryzen), где одно физическое ядро ​​может выступать в качестве нескольких логических ядер, активация обоих логических ядер увеличивает общую пропускную способность за счет снижения производительности на поток.

Таким образом, 1 минута процессорного времени на ядре, в котором простаивает HT-брат, может выполнить больше работы, чем когда другое логическое ядро ​​также было активным.

С активными обоими логическими ядрами современный Skylake или Ryzen может дать вам от 50 до 99% производительности однопоточности, если все ресурсы выполнения доступны для одного ядра, в полной зависимости от того, на каком коде выполняется каждая нить. (Если оба узких места в задержке FP добавляют и умножают с очень длинными цепочками зависимостей, переносимых циклами, которые не по порядку выполнения не могут видеть в прошлом, например, оба суммируют очень большие массивы в порядке со строгим FP, это лучший случай для HT Ни один из потоков не замедлит работу другого, поскольку пропускная способность добавления FP составляет от 3 до 8x задержек добавления FP.)

Но в худшем случае, если обе задачи сильно замедляются из-за пропусков кэша L1d, HT может даже потерять пропускную способность при одновременном запуске обоих на одном и том же ядре вместо запуска одной и другой.

...