Бенчмаркінг різних JVM та багато графіків

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Привіт, спільното! Нещодавно при розробці нового проєкту переді мною повстало питання вибору JVM. На сьогодні є великий вибір як повністю опенсорсних, так і пропрієтарних реалізацій та дистрибуцій, тож мені стало цікаво перевірити, чим вони відрізняються окрім ліцензій та технічної підтримки від вендорів. Результатами тестів вирішив поділитись і з вами. Для порівняння відібрано 10 найпопулярніших дистрибуцій JDK для 17 версії Java. А саме:

  • Amazon Corretto — Опенсорсний дистрибутив JDK від компанії Amazon, розповсюджується по ліцензії GPLv2 with CPE.
  • Alibaba Dragonwell — Дистрибутив від китайської компанії Alibaba, розповсюджується по такій самій ліцензії.
  • GraalVM CE — Модульний рантайм з підтримкою різних мов програмування. Основний модуль, що реалізує JDK, поставляється по ліцензії GPLv2 with CPE.
  • GraalVM EE — Пропрієтарне видання GraalVM. Ліцензія дозволяє безоплатне використання в особистих цілях.
  • Java SE (Oracle JDK) — Пропрієтарна JDK від Oracle. Розповсюджується під ліцензією Oracle No-Fee Terms and Conditions License.
  • Liberica JDK — Опенсорсна дистрибуція JDK від компанії BellSoft.
  • OpenJ9 — Альтернативна опенсорсна JDK від компанії Eclipse. На відміну від інших JDK з цього списку використовує повністю власну реалізацію JVM замість HotSpot. Розробники обіцяють на 42% швидший запуск порівняно з JVM на базі HotSpot.
  • OpenJDK — Класична опенсорсна JDK, що лежить в основі більшості JDK з цього списку. Розповсюджується по ліцензії GPLv2 with CPE.
  • Temurin (Adoptium) — Ще одна опенсорсна дистрибуція JDK від компанії Eclipse, на цей раз на базі OpenJDK зі звичайним HotSpot. Як і інші опенсорсні дистрибутиви, має ліцензію GPLv2 with CPE.
  • Zulu — Опенсорсна дистрибуція JDK від компанії Azul.

Для тестування я написав простенький бенчмарк, який тестує всі основні важливі для мого проєкту аспекти продуктивності JVM, окрім, хіба що, GC. Сам бенчмарк тестувався тільки на 64-бітному лінуксі, тож його роботу на інших ОС гарантувати не можу.

В рамках цього тесту бенчмарк виконував по 3 запуски JVM з метою зібрати середню статистику поміж запусками та знизити вплив зовнішніх факторів на результати. Також у деяких тестах бенчмерк проводить по 5 викликів тестового метода, аби проаналізувати вплив JIT-оптимізацій на результат.

Дисклеймер

Ця стаття — моє особисте мінідослідження, тому наведені тести покривають далеко не весь функціонал JVM. Також хочу зазначити, що числові показники у графіках та результатах бенчмарку слід використовувати лише для порівняння між собою результатів тестів, виконаних в однакових умовах і на однаковому залізі. Більшість наведених показників затримок трохи завищені через оверхед від бенчмарку.

Тести

І так, почнемо з найпростішого — часу затраченого на запуск. І тут одразу помічаємо дивні речі — OpenJ9, розробники якого обіцяли на 42% швидший за інші JVM старт, в реальності запускається в кілька разів довше. Заради справедливості хочу підмітити, що в графіку вказаний середній показник з 3 ітерацій бенчмарку, і найкращий результат OpenJ9 показав на третій ітерації, запустившись за 289 мілісекунд, що все ще надто багато.

Я довго намагався зрозуміти природу цього явища, і єдина ідея щодо можливої причини такої затримки — особливість мого бенчмарку, через яку час запуску рахується не після виклику main-функції, а в конструкторі об’єкта тесту «Startup», який в свою чергу викликається в статичному конструкторі main-класу. Можливо, OpenJ9 містить оптимізації, які впливають на чергу ініціалізації класів, але це лише теорія.

Серед інших JVM кращі результати показали OpenJDK та її модифікації.

По результатам тесту на використання ОЗП, помічаємо що всі дистрибуції HotSpot JVM впорались майже ідентично з рівнем споживання оперативної пам’яти приблизно 2МБ на старті і 20МБ при завршенні бенчмарку. OpenJ9 знов неприємно дивує, споживаючи аж 65МБ одразу після запуску JVM.

Далі проаналізуємо оверхед JIT компіляції. Фактично це різниця між часом, витраченим на перший та повторний запуск одного нескладного метода. Як бачимо, HotSpot витрачає на компіляцію метода в середньму від 2 до 4 мілісекунд. OpenJ9 демонструє трохи більші показники, але теж в рамках допустимого.

Тут можемо побачити результати заміру витрати часу на виконання доволі складного метода з великим циклом і математичними операціями. Цифри по осі X означають номер ітерації виклику метода в рамках однієї ітерації бенчмарку. OpenJ9 демонструє набагато меншу швидкість виконання, ніж JVM на базі HotSpot. Також на графіку можна побачити результат роботи оптимізацій JIT-компілятора: перший виклик метода займає найбільшу кількість часу, а з кожним наступним викликом час виконання зменшується.

Якщо відкинути результати OpenJ9 та подивитись на продуктивність інших JVM ближче, можемо побачити приблизно однакову картину.

Час відгуку JNI на першій ітерації у половини з протестованих JVM значно більший ніж у іншої. Цей показник може бути критичним наприклад при розробці ігор з використанням LWJGL, або просто програм, які в значній мірі полягаються на нативні бібліотеки.

Якщо відкинути час відгуку на першій ітерації, бачимо приблизно однакові результати для всіх JVM.

При вимірюванні часу, затраченого на виклик метода через рефлексію, несподівано бачимо відсутність значної різниці між першою та наступними ітераціями в OpenJ9.

Але варто лише відкинути результати першої ітерації, і ми бачимо, що інші реалізації JDK мають в цілому кращу продуктивність при роботі з рефлексією.

І останній на сьогодні тест — побайтове копіювання заповненого випадковими даними масиву байт розміром в 5 мегабайт з однієї змінної в іншу. Результат ви бачите самі.

Резюмуючи

Насправді виділити явного переможця в цих тестах складно. Проте за допомогою них можна побачити сильні та слабі сторони кожної JVM. В будь-якому випадку треба розуміти, що жодні синтетичні тести не можуть показати реальну картину продуктивності роботи JVM з саме вашим кодом та на вашому проєкті.

Для тих, хто бажає подивитись на точні цифри, надаю повні результати бенчмаркінга для кожної з JVM:

Сподобалась стаття? Натискай «Подобається» внизу. Це допоможе автору виграти подарунок у програмі #ПишуНаDOU

👍ПодобаєтьсяСподобалось10
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Я вимушений констатувати, що автор не досить добре розуміється на термінології, що також викликає сумніви щодо якості тесту. Отже,

найпопулярніших реалізацій JVM

йдемо до Wiki і дивимося які існують JVM en.wikipedia.org/...​_of_Java_virtual_machines. Далі, наведені приклади це ДИСТРИБУЦІЯ проекту OpenJDK. Я би міг пройтися по кожному лінку, але ось навіть доки по Corretto говорять, що воно НЕ JVM: aws.amazon.com/...​red-posts.sort-order=desc

Тому, я би рекомендував виправити статтю бо не існую а ні «Опенсорсна реалізація JDK», а ні «Опенсорсна JDK» і це легко довести лише подивившись з чого складається JDK простою командою:
java --list-modules

Далі, існую проект OpenJDK, існую декілька дистибуцій OpenJDK від Oracle, Azul, AWS, Zulu, etc. (різниця полягає лише у тому із ким ви підписуєте контракт на підтримку). Окремо існує «стандрат JVM», у складі проекту OpenJDK є HotSpotVM розроблена Sun і якою володіє Oracle, цю JVM використовують майже усі наведені включно із GraalVM CE/EE.

який тестує всі основні аспекти продуктивності JVM, окрім, хіба що, GC

 — після цього статтю можна далі й не читати, бо тест не враховую а ні AppCDS, а ні ZGC. Бо перший дає змогу виправляти проблему холодного старту, а новий GC — паузи менші за 10мс. Ба більше гото, якщо почитати спеціфікації на кожну вендор-специфічну дистрибуцію OpenJDK, то можна побачити, що вони відрізняються у конфігурації та типу GC (HotSportVM — G1, Azul — C4, у RedHat — Shenandoah GC) , тюнінгу та реалізації JIT-компілятору, наприклад Azul використовую Falcom LLMV, а Oracle OpenJDK — C2, у той час як GraalVM — взагалі використовую свій компілятор.

всі JVM на базі HotSpot

 — знову некорректне твердження яке потребує виправлення, бо HotSpotVM і є імплементацією JVM.

Далі, тест JNI не показовий взагалі, бо ця технологія застара і не оновлювалася дуже довгий час (не дивно, що з того часу вже були створені технлогіі типу JNA, JavaCPP). Треба робити тест на основі Foreign Function & Memory API (Project Panama) який базується на константних/статичних та динамічних визначеннях MethodHandle.

Далі, сам бенчмарк не є показовим взагалі, бо не використовую нативні інструменти для бенчмаркінгу JVM — github.com/...​icrobenchmark-harness-jmh.

Отже, висновок. Порівняння дистибуцій OpenJDK робилося на «якомусь» linux-сервері: а ні типу процессору, а ні тип диску, а ні кількість наявної оперативної пам’яті. Але це дрібниці. Сам бенчмарк написаний на Kotlin, тобно існє додатковий шар оверхеду при компіляції у байткод. На скільки це еффективно? Треба дивитися сам байткод і порівнювати із оригіналом написаним на Java. Навіть у такій формі тест не є показовим, бо усі OpenJDK тестувалися не у рівних умовах.

Дякую за пояснення щодо термінології. Трохи підредагував статтю, щоб не розповсюджувати дезінформацію :)
З приводу AppCDS, тестування GC та JNI все насправді просто — як я писав на початку статті, ці тести я робив під один конкретний проєкт. Проєкт доволі специфічний для Java, бо пов’язаний з геймдевом. Тестувати AppCDS не бачив особливого сенсу, бо у цьому тесті мене цікавив якраз показник часу затраченого на компіляцію класів. GC це дійсно важливий компонент продуктивності, але на поточному етапі проєкту у нас є достатній запас по часу на ігровий тік, що дозволяє принаймні поки що ігнорувати це питання. Тестували JNI тому, що саме він лежить під капотом у LWJGL, тож тут у нас не було особливого вибору що використовувати. JMH я не використав, бо з початку і не планував писати повноцінний бенчмарк, просто хотів швидко «на колінці» протестувати кілька показників. Може в майбутньому як буде вільний час, перепишу з його використанням.
Звісно що тест далеко не ідеальний і далеко не об’єктивний, проте я взагалі не знайшов чогось схожого українською мовою, тож вирішив опублікувати свої результати.

Непогана стаття. Думаю, якщо перекласти на англійську і закинути в реддіт, прийдет багато контрібуторів з форків, і почнуть розказувати, які в них чудові джвмки (насправді — ні).

Імхо основна причина усього зоопарку — чуваки не осилили ПР в основну репу джвм, бо там процес доволі зарегульований.

Від того «з ціллю» аж пересмикує. Чому не написати «з метою»?

Відредагував, дякую що зауважили, моя українська на жаль ще не ідеальна

Підписатись на коментарі