Проблема с производительностью Java в Oracle Linux

Я запускаю очень простой тест.

@Fork(value = 1, jvmArgs = { "--illegal-access=permit", "-Xms10G", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints", "-XX:ActiveProcessorCount=7",
        "-XX:+UseNUMA"
        , "-XX:+UnlockDiagnosticVMOptions", "-XX:DisableIntrinsic=_currentTimeMillis,_nanoTime",

        "-Xmx10G", "-XX:+UnlockExperimentalVMOptions", "-XX:ConcGCThreads=5", "-XX:ParallelGCThreads=10", "-XX:+UseZGC", "-XX:+UsePerfData", "-XX:MaxMetaspaceSize=10G", "-XX:MetaspaceSize=256M"}
)
    @Benchmark
    public String generateRandom() {
        return UUID.randomUUID().toString();
    }

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

На моем домашнем рабочем столе

Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz 12 Threads (hyperthreading enabled ), 64 GB Ram, "Ubuntu" VERSION="20.04.2 LTS (Focal Fossa)"
Linux homepc 5.8.0-59-generic #66~20.04.1-Ubuntu SMP Thu Jun 17 11:14:10 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Производительность с 7 потоками:

Benchmark                                            Mode  Cnt        Score       Error   Units
RulesBenchmark.generateRandom                       thrpt    5  1312295.357 ± 27853.707   ops/s

Flame Graph с результатом AsyncProfiler с 7 потоками дома введите здесь описание изображения< /а>

У меня проблема с Oracle Linux

Linux  5.4.17-2102.201.3.el8uek.x86_64 #2 SMP Fri Apr 23 09:05:57 PDT 2021 x86_64 x86_64 x86_64 GNU/Linux
Intel(R) Xeon(R) Gold 6258R CPU @ 2.70GHz with 56 Threads(hyperthreading disabled, the same when enabled and there is 112 cpu threads ) and 1 TB RAM I have half of performance (Even increasing threads) NAME="Oracle Linux Server" VERSION="8.4"

с 1 потоком у меня очень хорошая производительность:

Benchmark                                            Mode  Cnt        Score      Error   Units
RulesBenchmark.generateRandom                       thrpt    5  2377471.113 ± 8049.532   ops/s

Flame Graph с AsyncProfiler Result 1 Thread введите здесь описание изображения Но с 7 резьбой

Benchmark                                            Mode  Cnt       Score       Error   Units


RulesBenchmark.generateRandom                       thrpt    5  688612.296 ± 70895.058   ops/s

Flame Graph с AsyncProfiler Result Thread 7

введите здесь описание изображения

Возможно, это проблема NUMA, потому что есть 2 сокета, а система настроена только на 1 узел NUMA numactl --hardware

available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
node 0 size: 1030835 MB
node 0 free: 1011029 MB
node distances:
node   0 
  0:  10 

Но после отключения некоторых потоков процессора с помощью:

for i in {12..55}
do
 # your-unix-command-here
  echo '0'| sudo tee /sys/devices/system/cpu/cpu$i/online
done

Производительность немного улучшилась, не намного.

Это просто очень простой тест. На сложном тесте с реальным кодом даже стоит, много времени уходит на .annobin___pthread_cond_signal.start

Я также развернул бродячий образ с той же версией Oracle Linux и версией ядра на своем домашнем рабочем столе и запустил его с 10 потоками процессора, и производительность была почти такой же (~ 1 млн операций в секунду), как на моем рабочем столе. Так что дело не в ОС или ядре, а в какой-то конфигурации

Протестировано с несколькими версиями jDK и поставщиками (jdk 11 и выше). Производительность очень низкая при использовании OpenJDK 11 из дистрибутива YUM, но незначительная.

Не подскажете, заранее спасибо


person Dimitri Gamkrelidze    schedule 01.07.2021    source источник


Ответы (1)


По сути, ваш бенчмарк проверяет пропускную способность SecureRandom. Реализация по умолчанию: synchronized (точнее, реализация по умолчанию смешивает форму ввода /dev/urandom и указанный выше провайдер).

Парадокс заключается в том, что большее количество потоков приводит к большему количеству конфликтов и, следовательно, к снижению общей производительности, поскольку основная часть алгоритма в любом случае находится под глобальной блокировкой. Async-profiler действительно показывает, что узким местом является синхронизация на Java-мониторе: __lll_unlock_wake, __pthread_cond_wait, __pthread_cond_signal — все происходит от этой синхронизации.

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

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

  • rdrand
  • OpenSSL
  • необработанный /dev/urandom доступ и т. д.
person apangin    schedule 02.07.2021
comment
Андрей, Большое спасибо, что нашли время и посмотрели на мой вопрос. Как я уже сказал, это всего лишь простой тест. У меня есть securerandom.source=file:/dev/urandom. Если вы посмотрите на stackoverflow.com/questions/67845210/drools-performance, там та же проблема. Любой тест, который я делаю на этом сервере, показывает почти 3-кратную низкую производительность. - person Dimitri Gamkrelidze; 03.07.2021
comment
@DimitriGamkrelidze В отличие от связанного вопроса, этот выглядел достаточно конкретно - надеюсь, я ответил о конкретной проблеме масштабируемости SecureRandom . Если ваша первоначальная проблема не связана с RNG, это означает, что ваш минимальный пример недостаточно минимален. Начните с теста 2+2. Это также действительно в 3 раза медленнее? - person apangin; 03.07.2021
comment
Вы правы, этот пример не достаточно прост для измерения производительности, если вы видели, я написал, что может быть это не очень просто, потому что использует рандом, но такая же проблема есть и в любых других тестах с java. Спасибо за ответ. Вот суть gist.github.com/ditogam/eab68c5ea69f49e6decd6ed85952df9b, и вы увидите это с AtomicInteger у него нет проблем, с синхронизацией и блокировкой ReentrantLock результаты значительно различаются. - person Dimitri Gamkrelidze; 03.07.2021
comment
Тем не мение. Спасибо, что уделили время подробному ответу, и вы ответили так, как видели. Это моя вина, что я привел плохой пример для вопроса :) - person Dimitri Gamkrelidze; 03.07.2021
comment
@DimitriGamkrelidze Фактически, из вашего теста можно сделать один хороший вывод: атомарность масштабируется намного лучше, чем блокировки. Нет особого смысла запускать изначально однопоточный тестовый пример в 7 потоков, кроме как для получения доказательства того, что конфликт блокировок — отстой. Вы наверняка хотели узнать, почему на сервере хреновее больше, чем на домашнем ноуте, но на мой взгляд, это не имеет значения. Это может быть частота ЦП, стоимость доступа к памяти, аудит системных вызовов или другие случайные вещи - не имеет значения. Конкуренция — это плохо по определению, поэтому постарайтесь уменьшить конкуренцию, а лучше вообще избавиться от нее. - person apangin; 04.07.2021