Java — производительность пула объектов по сравнению с созданием экземпляра нового объекта

В настоящее время я пытаюсь создать некоторую оптимизацию выполнения кода для конкурса и рассматривал шаблон ObjectPool, чтобы способствовать повторному использованию объекта вместо создания экземпляра нового объекта.

Я собрал небольшой проекттолько тестовый класс), чтобы исследовать некоторые вещи, которые я вижу и не понимаю. .

Что я делаю:

  • сравните создание очень простых объектов за 5 000 000 итераций с использованием операций new() и Pool.get()
  • play around three axes, running all tests with and without:
    • a "warmup" that runs the loop once before doing the measurements
    • назначение вновь созданного объекта локальной переменной и использование ее для некоторых вычислений
    • использование фиксированных и случайных параметров в качестве аргументов

У меня есть следующие результаты: Figures are for new instantiation vs with object pool for 5 000 000 iterations without_warmup_without_new_object_use_with_random_parameters: 417 vs 457 without_warmup_without_new_object_use_with_fixed_parameters: 11 vs 84 without_warmup_with_new_object_use_with_random_parameters: 515 vs 493 without_warmup_with_new_object_use_with_fixed_parameters: 64 vs 90 with_warmup_without_new_object_use_with_random_parameters: 284 vs 419 with_warmup_without_new_object_use_with_fixed_parameters: 8 vs 55 with_warmup_with_new_object_use_with_random_parameters: 410 vs 397 with_warmup_with_new_object_use_with_fixed_parameters: 69 vs 82

Что я из этого замечаю:

  • Использование фиксированных параметров имеет огромное значение при создании экземпляра нового объекта без его повторного использования. Я предполагаю, что компилятор выполнял какую-то оптимизацию и обнаружил, что побочных эффектов нет, и он полностью удалит создание экземпляра объекта, но сравнение производительности с пустым циклом показывает, что что-то все еще происходит.
  • Использование фиксированных параметров оказывает значительное влияние (хотя и менее выраженное) на скорость new Object(), делая его быстрее, чем версия пула объектов в некоторых случаях.
  • Пул объектов работает быстрее в "реальных" сценариях (т.е. повторное использование новых объектов и несколько случайных параметров), но не в большинстве из них, что также указывает на оптимизацию компилятора.

Я ищу здесь, чтобы понять эти результаты и получить указатели на документы/книги, которые я мог бы прочитать, чтобы получить хорошее представление о том, что происходит за кулисами в этих случаях.

Спасибо!


person Sébastien Tromp    schedule 28.01.2017    source источник
comment
Похоже, вы в основном сравниваете свои тесты. Используйте проверенную систему сравнительного анализа, например JMH. Вы уже видели, насколько коварной может быть JIT.   -  person Boris the Spider    schedule 28.01.2017
comment
Кроме того, различия между тем, что вы считаете тестом со случайными параметрами, и тем, что вы считаете тестом с фиксированными параметрами, можно отнести полностью к стоимости вызова random.nextDouble(), а не к любой оптимизации компилятора/JIT, которую вы предполагаете.   -  person Mike Nakis    schedule 28.01.2017
comment
В любом случае, это бесполезное упражнение: конечно, ваш пул похоже будет работать быстрее, чем выделение памяти, но тогда ваш пул будет слишком упрощенным, он на самом деле не работает (заполните его и вы начинаете повторно использовать ранее выделенные объекты), и это ничего не освобождает. К тому времени, когда вы добавите к нему достаточно кода, чтобы исправить это, почти гарантировано, что он будет работать хуже, чем выделение памяти.   -  person Mike Nakis    schedule 28.01.2017
comment
Спасибо за совет, попробую с настоящим профайлером разобраться в пункте 3) и общей разнице в перфах. По-прежнему открыты для любых советов относительно фиксированных и нефиксированных параметров. @MikeNakis просто чтобы убедиться, что мы говорим об одном и том же, меня интересуют только различия, в частности, как фиксированные параметры влияют на сравнение повторного использования объекта с и без повторного использования объекта для новых экземпляров, в то время как это очень небольшое влияние на пул объектов   -  person Sébastien Tromp    schedule 28.01.2017
comment
@MikeNakis fill it up and you start reusing objects previously allocated Не уверен, что понял, что вы имеете в виду. Насколько я вижу, пул заполнен и повторно использует предметы. Что касается освобождения объектов, я хочу рассмотреть это позже. Мой вариант использования довольно прост, поэтому флага на объекте (или клонирования нескольких объектов, которые я хочу сохранить) может быть достаточно.   -  person Sébastien Tromp    schedule 28.01.2017
comment
Я имею в виду, что если количество распределений превышает POINT_POOL_SIZE, вы будете повторно использовать баллы, которые могут быть уже использованы. Чего, собственно, и следовало ожидать, поскольку у вас нет даже понятия об освобождении. Как только вы добавите концепцию освобождения, все станет намного сложнее. Среда выполнения языка Java давно решила все эти проблемы при реализации оператора new.   -  person Mike Nakis    schedule 28.01.2017
comment
Once you add the concept of freeing, things will become a lot more complicated: понял. В данном конкретном случае объекты создаются для короткой обработки и впоследствии могут быть повторно использованы, поэтому нужно ли их явно освобождать?   -  person Sébastien Tromp    schedule 28.01.2017
comment
если вы не освободите их явно (отметьте их как доступные в пуле), то, как только вы достигнете конца своего пула, вы не сможете переместиться к началу пула (как вы делаете сейчас), потому что если вы это сделаете что тогда вы будете повторно использовать объекты, которые уже используются.   -  person Mike Nakis    schedule 28.01.2017
comment
надеюсь это помогло   -  person Jan Hanson    schedule 29.01.2017


Ответы (1)


Фиксированные параметры

Как упоминалось в комментарии Майка Накиса, разница между вашими тестами со случайными параметрами и тестами с фиксированными параметрами полностью обусловлена за счет генерации случайного числа, более честным тестом может быть создание массива из 10 миллионов записей случайных целых чисел (по 1 для каждого параметра, необходимого для инициализации Point) перед запуском цикла и сравнением этого с массивом из 10 миллионов записей чисел выбранные вами (т.е. 1 и 2), таким образом, вы сравниваете подобное, не включая расходы на генерацию случайных чисел в результаты вашего теста.

Спектакль

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

if (pointIndex >= POINT_POOL_SIZE) {
            pointIndex = pointIndex - POINT_POOL_SIZE;
            totalPointLoops++;
        }

а также индексирование массива требует больше времени на выполнение, чем требуется вашему объекту Point для инициализации.

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

актуальная информация

Вот несколько хороших ссылок на информацию о пулах объектов в Java:

person Jan Hanson    schedule 28.01.2017
comment
Спасибо Ян! Я последовал общему мнению и сделал правильное профилирование (некоторое время я боролся с VisualVM, но в итоге остановился на пробной версии YourKit), и профилирование показало, что пул объектов на самом деле немного быстрее, даже для таких простых объектов. Спасибо за вашу помощь! - person Sébastien Tromp; 01.02.2017