Dlaczego JMH
Unikanie naiwnych błędów w benchmarkach
Dlaczego JMH to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Dlaczego JMH
Java Microbenchmark Harness (JMH) to standardowe narzędzie do pomiaru wydajności małych fragmentów kodu Java. Ręcznie tworzone pętle pomiarowe niemal zawsze podają nieprawidłowe wyniki, ponieważ JVM jest zaawansowanym środowiskiem uruchomieniowym wykonującym optymalizacje.
JMH służy do eliminowania tych problemów.
Naiwny benchmark
Typowa pierwsza próba polega na opakowaniu pętli w wywołania System.nanoTime(). Wygląda to rozsądnie, ale w przypadku mikrobenchmarków jest poważnie błędne.
public class Main {
static int compute(int n) { return n * n + 7; }
public static void main(String[] args) {
long start = System.nanoTime();
int sink = 0;
for (int i = 0; i < 1_000_000; i++) sink = compute(i);
long elapsed = System.nanoTime() - start;
System.out.println("ns: " + elapsed + " sink=" + sink);
}
}Problem 1: Rozgrzewka JIT
JVM uruchamia się w trybie interpretowanym i dopiero po tysiącach wywołań kompiluje często wykonywane metody do kodu natywnego. Naiwny benchmark mierzy wolną fazę interpretowaną połączoną z szybką fazą skompilowaną, co prowadzi do pozbawionych znaczenia średnich.
JMH rozwiązuje ten problem za pomocą oddzielnej fazy rozgrzewki, której wyniki są odrzucane.
Problem 2: Eliminacja martwego kodu
Jeśli obliczony wynik nie jest nigdy używany, JIT może całkowicie usunąć obliczenia. Benchmark mierzy wtedy pustą pętlę.
JMH udostępnia zwracanie wartości oraz obiekt Blackhole, aby temu zapobiec.
Problem 3: Constant folding
Jeśli dane wejściowe są stałymi znanymi w czasie kompilacji, JIT oblicza wynik raz i używa go ponownie. W poniższym przykładzie kompilator może zastąpić całą pętlę pojedynczą stałą.
JMH używa obiektów @State, dzięki czemu optymalizator nie zna wartości wejściowych.
public class Main {
public static void main(String[] args) {
// 2 * 21 is constant; the JIT folds it to 42
int result = 2 * 21;
System.out.println(result);
}
}Problem 4: Optymalizacje pętli
JIT rozwija pętle, wynosi kod niezmienny poza pętle i wektoryzuje operacje. Ręcznie napisana pętla mierzy wtedy te optymalizacje, a nie operację, którą zamierzali Państwo przetestować.
JMH zastępuje Państwa pętlę starannie kontrolowanymi licznikami iteracji, którymi zarządza samodzielnie.
Problem 5: On-stack replacement
Długo wykonywana pętla w main może zostać skompilowana w trakcie wykonywania (on-stack replacement), co prowadzi do charakterystyki wydajności innej niż w przypadku normalnie skompilowanej metody. Powoduje to nieprzewidywalne zniekształcenie wyników.
Co zapewnia JMH
- Oddzielne, odrzucane iteracje rozgrzewkowe.
- Wiele iteracji pomiarowych wraz ze statystykami.
- Uruchamianie w nowych JVM, aby uniknąć zanieczyszczenia profili.
- Blackhole i zwracanie wyników chroniące przed eliminacją martwego kodu.
- Obiekty @State zapobiegające constant folding.
Jak uruchomić benchmark
JMH jest oddzielną zależnością (org.openjdk.jmh) i zwykle uruchamia się je za pomocą procesora adnotacji oraz kompilacji Maven/Gradle, która tworzy wykonywalny plik JAR. Benchmarków nie uruchamia się z użyciem zwykłego main, tak jak zwykłego kodu.
Statystyki mają znaczenie
JMH podaje nie tylko średnią, lecz także błąd / przedział ufności obliczony na podstawie iteracji i forków. Wynik 42.0 +/- 1.3 ns/op informuje zarówno o wartości centralnej, jak i o skali jej zmienności — jest to niezbędne, aby ufać pomiarowi.
Kiedy sięgnąć po JMH
JMH należy używać podczas porównywania dwóch implementacji często wykonywanej ścieżki, weryfikowania optymalizacji lub mierzenia operacji trwających od nanosekund do mikrosekund. W przypadku zadań trwających całe sekundy, takich jak operacje wejścia-wyjścia czy komunikacja sieciowa, zwykle wystarcza pomiar czasu zegarowego.
Szybki test
Proszę sprawdzić zrozumienie, dlaczego JMH jest potrzebne.
Podsumowanie
Nauczyli się Państwo, dlaczego mikrobenchmarki wymagają odpowiedniego narzędzia:
- JVM przechodzi rozgrzewkę: kod interpretowany staje się skompilowany dopiero po wielu wywołaniach.
- Eliminacja martwego kodu i constant folding mogą usuwać obliczenia lub wykonywać je z wyprzedzeniem.
- Optymalizacje pętli i on-stack replacement zniekształcają wyniki ręcznie napisanych pętli.
- JMH dodaje rozgrzewkę, iteracje pomiarowe, forkowanie, Blackhole i statystyki.
- Warto po nie sięgnąć podczas mierzenia często wykonywanych ścieżek trwających nanosekundy.
Często zadawane pytania
Czy lekcja „Dlaczego JMH” jest bezpłatna?
Tak — pełny tekst „Dlaczego JMH” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Dlaczego JMH”?
Unikanie naiwnych błędów w benchmarkach Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Dlaczego JMH”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.