Jak napisać poprawny mikro-test porównawczy w Javie?

Feb 03 2009

Jak napisać (i uruchomić) prawidłowy mikro-test w Javie?

Szukam przykładów kodu i komentarzy ilustrujących różne rzeczy do przemyślenia.

Przykład: Czy benchmark powinien mierzyć czas / iterację lub iteracje / czas i dlaczego?

Powiązane: Czy benchmarking stopera jest akceptowalny?

Odpowiedzi

802 12revs,12users61%EugeneKuleshov Feb 05 2009 at 03:49

Wskazówki dotyczące pisania mikro-benchmarków od twórców Java HotSpot :

Zasada 0: Przeczytaj renomowany artykuł na temat maszyn JVM i mikro-testów porównawczych. Dobry jest Brian Goetz, 2005 . Nie oczekuj zbyt wiele od mikro-benchmarków; mierzą tylko ograniczony zakres charakterystyk wydajności maszyny JVM.

Zasada 1: Zawsze uwzględniaj fazę rozgrzewki, która uruchamia jądro testowe przez cały czas, wystarczającą do wyzwolenia wszystkich inicjalizacji i kompilacji przed fazami czasowymi. (Mniej iteracji jest w porządku w fazie rozgrzewki. Praktyczna zasada to kilkadziesiąt tysięcy iteracji pętli wewnętrznej).

Zasada nr 2: Zawsze uruchamiaj z -XX:+PrintCompilation, -verbose:gcitd, więc można sprawdzić, że kompilator i inne części JVM nie robią nieoczekiwany prace podczas fazy rozrządu.

Reguła 2.1: Wydrukuj komunikaty na początku i na końcu faz odliczania i rozgrzewki, abyś mógł sprawdzić, czy nie ma wyjścia z Reguły 2 podczas fazy odliczania.

Zasada 3: Bądź świadomy różnicy między -clienti -server, OSR a zwykłymi kompilacjami. -XX:+PrintCompilationFlaga informuje OSR składanki z co-znak oznaczający zakaz początkowy punkt wejścia, np Trouble$1::run @ 2 (41 bytes). Preferuj serwer od klienta i zwykły od OSR, jeśli zależy ci na najlepszej wydajności.

Zasada 4: Uważaj na efekty inicjalizacji. Nie drukuj po raz pierwszy w fazie synchronizacji, ponieważ drukowanie ładuje i inicjuje klasy. Nie ładuj nowych klas poza fazą rozgrzewki (lub końcową fazą raportowania), chyba że testujesz ładowanie klas w szczególności (w takim przypadku ładuj tylko klasy testowe). Zasada 2 to pierwsza linia obrony przed takimi efektami.

Zasada 5: Należy pamiętać o skutkach dezoptymalizacji i rekompilacji. Nie bierz żadnej ścieżki kodu po raz pierwszy w fazie taktowania, ponieważ kompilator może niepotrzebnie skompilować kod, opierając się na wcześniejszym optymistycznym założeniu, że ścieżka nie będzie w ogóle używana. Zasada 2 to pierwsza linia obrony przed takimi efektami.

Zasada 6: Korzystaj z odpowiednich narzędzi, aby czytać w myślach kompilatora i spodziewaj się zaskoczenia kodem, który tworzy. Sprawdź sam kod, zanim utworzysz teorie na temat tego, co sprawia, że ​​coś jest szybsze lub wolniejsze.

Zasada 7: Zmniejsz szum w swoich pomiarach. Uruchom test porównawczy na cichym komputerze i uruchom go kilka razy, odrzucając wartości odstające. Służy -Xbatchdo serializacji kompilatora z aplikacją i rozważ ustawienie, -XX:CICompilerCount=1aby zapobiec równoległemu uruchomieniu kompilatora. Postaraj się jak najlepiej zmniejszyć narzut GC, ustaw Xmx(wystarczająco duży) równa się Xmsi użyj, UseEpsilonGCjeśli jest dostępny.

Zasada 8: Użyj biblioteki do swojego testu porównawczego, ponieważ jest prawdopodobnie bardziej wydajna i została już debugowana tylko w tym celu. Takich jak JMH , Caliper lub doskonałe testy porównawcze UCSD Billa i Paula dla Javy .

244 AravindYarram Dec 19 2010 at 06:35

Wiem, że to pytanie zostało oznaczone jako odpowiedź, ale chciałem wspomnieć o dwóch bibliotekach, które pomagają nam pisać mikro testy porównawcze

Suwmiarka od Google

Samouczki dla początkujących

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

JMH z OpenJDK

Samouczki dla początkujących

  1. Unikanie problemów związanych z testami porównawczymi w JVM
  2. Używanie JMH do Java Microbenchmarking
  3. Wprowadzenie do JMH
88 JonSkeet Feb 03 2009 at 00:46

Ważne rzeczy dotyczące testów porównawczych Java to:

  • Rozgrzać JIT najpierw przez uruchomienie kodu kilka razy zanim rozrządu go
  • Upewnij się, że działasz wystarczająco długo, aby móc zmierzyć wyniki w ciągu kilku sekund lub (lepiej) dziesiątek sekund
  • Chociaż nie możesz wywoływać System.gc()między iteracjami, dobrym pomysłem jest uruchamianie go między testami, aby każdy test miał „czystą” pamięć do pracy. (Tak, gc()to bardziej wskazówka niż gwarancja, ale z mojego doświadczenia wynika, że ​​jest bardzo prawdopodobne , że naprawdę zbierze śmieci).
  • Lubię wyświetlać iteracje i czas oraz wynik czasu / iteracji, który można przeskalować w taki sposób, że „najlepszy” algorytm otrzymuje wynik 1,0, a pozostałe są oceniane w sposób względny. Oznacza to, że możesz uruchamiać wszystkie algorytmy przez długi czas, zmieniając zarówno liczbę iteracji, jak i czas, ale nadal uzyskując porównywalne wyniki.

Jestem właśnie w trakcie blogowania o projektowaniu frameworka do testów porównawczych w .NET. Mam kilka z wcześniejszych postów , które mogą być w stanie dać ci kilka pomysłów - nie wszystko będzie właściwe, oczywiście, ale niektóre z nich mogą być.

48 assylias Apr 03 2013 at 19:32

jmh to najnowszy dodatek do OpenJDK i został napisany przez niektórych inżynierów wydajności z Oracle. Z pewnością warte obejrzenia.

JMH to zestaw narzędzi Java do tworzenia, uruchamiania i analizowania testów porównawczych nano / mikro / makr napisanych w języku Java i innych językach przeznaczonych dla maszyny JVM.

Bardzo ciekawe informacje zakopane w przykładowych komentarzach do testów .

Zobacz też:

  • Unikanie problemów związanych z testami porównawczymi w JVM
  • Dyskusja na temat głównych mocnych stron jmh .
23 PeterLawrey Feb 03 2009 at 02:54

Czy benchmark powinien mierzyć czas / iterację lub iteracje / czas i dlaczego?

To zależy od tego , co próbujesz przetestować.

Jeśli interesuje Cię opóźnienie , użyj czasu / iteracji, a jeśli interesuje Cię przepustowość , użyj iteracji / czasu.

16 Kip Feb 03 2009 at 00:57

Jeśli próbujesz porównać dwa algorytmy, wykonaj co najmniej dwa testy porównawcze dla każdego, zmieniając kolejność. to znaczy:

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

Znalazłem pewne zauważalne różnice (czasami 5-10%) w czasie działania tego samego algorytmu w różnych przebiegach.

Upewnij się również, że n jest bardzo duże, tak aby czas wykonania każdej pętli wynosił co najmniej 10 sekund. Im więcej iteracji, tym bardziej znaczące liczby w czasie testu porównawczego i tym bardziej wiarygodne są dane.

15 PeterŠtibraný Feb 03 2009 at 01:00

Upewnij się, że w jakiś sposób korzystasz z wyników obliczonych w kodzie testowanym. W przeciwnym razie twój kod może zostać zoptymalizowany.

13 Mnementh Feb 03 2009 at 00:46

Istnieje wiele możliwych pułapek związanych z pisaniem mikro-testów w Javie.

Po pierwsze: musisz obliczyć z różnego rodzaju zdarzeniami, które zajmują mniej więcej losowy czas: zbieranie śmieci, efekty buforowania (systemu operacyjnego dla plików i procesora dla pamięci), IO itp.

Po drugie: nie można ufać dokładności mierzonych czasów w bardzo krótkich odstępach czasu.

Po trzecie: JVM optymalizuje kod podczas wykonywania. Tak więc różne uruchomienia w tej samej instancji JVM staną się coraz szybsze.

Moje zalecenia: spraw, aby test testowy działał przez kilka sekund, co jest bardziej niezawodne niż czas wykonywania w milisekundach. Rozgrzej JVM (co oznacza uruchomienie testu porównawczego co najmniej raz bez mierzenia, czy maszyna JVM może przeprowadzać optymalizacje). I uruchom swój benchmark wiele razy (może 5 razy) i weź medianę-wartość. Uruchom każdy mikro-test w nowej instancji JVM (wywołanie dla każdego testu porównawczego nowej Javy), w przeciwnym razie efekty optymalizacji maszyny JVM mogą wpłynąć na późniejsze uruchamianie testów. Nie wykonuj rzeczy, które nie są wykonywane w fazie rozgrzewki (ponieważ może to spowodować załadowanie klasy i ponowną kompilację).

8 SpaceTrucker Jan 21 2013 at 21:04

Należy również zauważyć, że podczas porównywania różnych wdrożeń ważne może być przeanalizowanie wyników mikro-benchmarku. Dlatego należy przeprowadzić test istotności .

Dzieje się tak, ponieważ implementacja Amoże być szybsza podczas większości przebiegów testu porównawczego niż implementacja B. Ale Amoże również mieć wyższy spread, więc zmierzona korzyść z wydajności Anie będzie miała żadnego znaczenia w porównaniu z B.

Dlatego ważne jest również, aby poprawnie napisać i uruchomić mikro-test, ale także poprawnie go przeanalizować.

8 SinaMadani Mar 20 2017 at 02:21

Aby dodać do innych doskonałych rad, powinienem również pamiętać o następujących kwestiach:

W przypadku niektórych procesorów (np. Serii Intel Core i5 z TurboBoost) temperatura (i liczba aktualnie używanych rdzeni, a także procent ich wykorzystania) wpływa na szybkość zegara. Ponieważ procesory są taktowane dynamicznie, może to wpłynąć na wyniki. Na przykład, jeśli masz aplikację jednowątkową, maksymalna prędkość zegara (z TurboBoost) jest wyższa niż w przypadku aplikacji korzystającej ze wszystkich rdzeni. Może to zatem kolidować z porównaniami wydajności jedno- i wielowątkowej w niektórych systemach. Pamiętaj, że temperatura i napięcia również wpływają na to, jak długo utrzymywana jest częstotliwość turbo.

Być może jest to bardziej fundamentalnie ważny aspekt, nad którym masz bezpośrednią kontrolę: upewnij się, że mierzysz właściwą rzecz! Na przykład, jeśli używasz System.nanoTime()do testowania określonego fragmentu kodu, umieść wywołania przypisania w miejscach, które mają sens, aby uniknąć mierzenia rzeczy, które Cię nie interesują. Na przykład nie rób:

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

Problem polega na tym, że nie otrzymujesz od razu czasu zakończenia po zakończeniu kodu. Zamiast tego wypróbuj następujące rozwiązania:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");
7 Yuriy Dec 19 2010 at 06:22

http://opt.sourceforge.net/Java Micro Benchmark - zadania kontrolne wymagane do określenia porównawczych charakterystyk wydajności systemu komputerowego na różnych platformach. Może służyć do kierowania decyzjami optymalizacyjnymi i porównywania różnych implementacji języka Java.