Profilery kłamią

Poszukując metod na zwiększenie wydajności programu do automatycznego doboru indeksów w bazach danych, który piszę w ramach swojego doktoratu, postanowiłem użyć profilera. Zawsze wydawało mi się, że profilowanie jest proste - podłączamy profiler do naszej aplikacji, czekamy aż uzbiera odpowiednio dużo danych i już po chwili z ładnych wykresów wiemy co i ile czasu w programie się wykonuje, i co warto optymalizować. Niestety, jak się wkrótce przekonałem, rzeczywistość nie jest taka piękna.

Pierwszy problem jaki się pojawił, to "jaki profiler wybrać"? Profilerów dla Javy jest mnóstwo, stąd zacząłem najpierw od tych darmowych. Profiler4J wyglądał obiecująco. Ściągnąłem, zainstalowałem i faktycznie po paru minutach mogłem nacieszyć się ładnym grafem wywołań, z ciężkimi metodami pokolorowanymi intensywnie na czerwono, z lekkimi - na bladoróżowo bądź biało.
Przystąpiłem więc do optymalizacji, oczekując dużego przyspieszenia, ponieważ profiler raportował 90% czasu spędzanego w jednej metodzie. No i tu spotkała mnie przykra niespodzianka. Przyspieszenie było niewielkie.
Pomyślałem, że może moje umiejętności pisania wydajnego kodu w Javie nie są najlepsze. Dopisałem więc kilka wywołań System.nanoTime() tu i ówdzie, i okazało się, że nowa wersja tej metody faktycznie wykonuje się kilka razy szybciej niż poprzednia wersja, więc całość programu powinna też przyspieszyć kilkukrotnie... no chyba, że zmierzone wcześniej 90% jest nieprawdziwe.

Zacząłem się dokładniej przyglądać profilerowi - jego ustawieniom oraz zasadzie działania. Spodziewałem się oczywiście, że wyniki podawane przez to narzędzie, podobnie jak przez każde inne narzędzie pomiarowe, są obarczone jakimś błędem, ale nie sądziłem, że ów błąd może być tak duży, żeby dla pojedynczej metody raportować wielokrotnie za duży udział. Dlatego wypróbowałem inny profiler (konkretnie był to profiler wbudowany w VisualVM) i niestety tu wyniki były dość zgodne z wynikami z Profiler4J. Czyli albo coś jest takiego w moim programie, co oszukuje profilery, albo profilery są do bani. :D

Zagadka szybko się rozwiązała po wykonaniu kilku prostych eksperymentów. Większość profilerów umożliwia określenie, które pakiety mają być uwzględniane, a które pominięte w pomiarach. Dzięki wypróbowaniu różnych ustawień doszedłem do następujących wniosków:

  1. Szybkość działania profilowanego programu drastycznie zmienia się w zależności od liczby profilowanych metod. Profilując wszystko udało mi się doprowadzić do 20-krotnego spowolnienia aplikacji.
  2. Profilowanie krótkich, często wywoływanych metod spowalnia dużo silniej niż profilowanie kolosów na 3 ekrany.
  3. Profiler mierzy wykonywanie już spowolnionych przez siebie metod.

Wcześniej wspomniana problematyczna metoda właśnie wywoływała wielokrotnie bardzo małe, często jednolinijkowe metody (ale nie tylko gettery i settery), stąd profiler doświadczył ją dużo bardziej niż inne części kodu. Ogólnie problem jest poważny, bo żaden profiler nie potrafi nawet oszacować, jak bardzo to zjawisko może zakłócać podawane wyniki. A jak mawiał mój fizyk "Wynik pomiaru bez oszacowania błędu jest bezużyteczny".

Zacząłem zastanawiać się, czy nie istnieje jakiś inny sposób profilowania - bez ingerencji w kod. Znalazłem
to: JSamp oraz to: hprof. Nie mają wprawdzie GUI, nie robią grafów ani drzew wywołań, ale przynajmniej nie spowalniają programu i nie kłamią. Z drugiej strony są też bardzo rozbudowane komercyjne profilery, które zrobią wszystko łącznie z rozwieszeniem prania i wyprowadzeniem psa na spacer, ale też nie do końca to mi było potrzebne (to trochę jak kupowanie SUV, żeby jeździć do pracy w zatłoczonym centrum). To wszystko skłoniło mnie do napisania własnego, lekkiego profilera, który mam nadzieję zaoszczędzi wielu frustracji nie tylko mnie. Zapraszam wszystkich do testów.

Comments

Opcje przeglądania komentarzy

Wybierz preferowany sposób wyświetlania komentarzy i klinij na "Zapisz ustawienia", aby aktywować zmiany.
jpospychala's picture

VisualVM?

jestem ciekaw porównania Twojego stackprobe z SUN VisualVM. Tamci też się piszą o lightweight profiling i działają poprzez JMX

VisualVMa testowałem z JDK 6

VisualVMa testowałem z JDK 6 u 10. Działa częściowo przez JMX - żeby uzyskać informacje o wątkach i ogólnych statystykach JVM, tak samo jak JConsole (i to chodzi i wygląda b. ładnie - dużo ładniej niż JConsole), ale już profilery działają poprzez instrumentację.
Dla upewnienia się właśnie przed chwilą odpaliłem jeszcze raz i podłączyłem do Eclipse'a - najlepsze jest to, że w czasie instrumentacji nie pokazuje nawet sensownego progressbara tylko cały czas napis" connecting to the target JVM...". Profiler4j przynajmniej to miał ładnie zrobione. A, i Eclipse wisi mi już przez cały czas jak piszę ten tekst :( Do profilowania systemów produkcyjnych to strach tego użyć. :D

Poza tym kiedyś VisualVm zachowywał się czasem tak jakby nie zauważał pewnych klas w moim programie. Nie wiem, czy to był bug, czy po prostu on tak ma - teraz ostatnio nie sprawdzałem (ale od update 10 do 14 chyba dużo się w nim nie zmieniło).

Jeszcze cytat ze strony VisualVM, żebym nie został posądzony o nieprawdziwe informacje na temat konkurencji ;)


This profile command returns detailed data on method-level CPU performance (execution time), showing the total execution time and number of invocations for each method. When analyzing application performance, VisualVM instruments all of the methods of the profiled application. Threads emit the "method entry" event when entering a method and generate the corresponding "method exit" event when exiting the method. Both of these events contain timestamps. This data is processed in real time.

Czyli "lightweight profiling" to taki "chłyt marketingowy" jak to w pewnym skeczu było.
Ciekawe ile razy więcej czasu zajmuje wyemitowanie 2 zdarzeń zawierających timestamp w porównaniu z jednolinijkowym getterem/setterem, który został zinlinowany przez HotSpot.

dtrace?

Próbowałeś profilować (open)solarisowym dtrace?
Słoneczko strasznie się chwali, jakie to świetne narzędzie, może udałoby Ci się ciekawego coś z niego wykrzesać.

Nie próbowałem. Z opisu

Nie próbowałem. Z opisu wygląda na bardzo zaawansowane narzędzie, ale ugryźć to trochę trudno bez czytania iluś stron manuala. Profiler, który można programować... niezłe. Ale to nie ten target. Mnie było potrzebne coś prostego i wygodnego w użyciu co powie mi, które części kodu ile czasu się wykonują, i ile zyskam jak je zoptymalizuję. Zwłaszcza w sytuacji, gdy nie mam JEDNEGO WYRAŹNEGO wąskiego gardła (bo do tego Netbeans mi wystarczał przez jakiś czas).

BTW: Demko StackProbe'a na stronie działa? Widzę, że ludzie wchodzą i uruchamiają, ale nie mam szans sprawdzić, czy wszystko się wszystkim uruchamia jak trzeba. Zastanawia mnie, czy wymaganie na JDK nie jest jakąś dużą przeszkodą - Windows domyślnie ma instalowane JRE, a to za mało - bo brakuje tools.jar.

Jeśli będzie wyraźne zainteresowanie, to będę StackProbe'a rozwijał - mam w zanadrzu dodanie też części śledzącej wycieki pamięci oraz API, żeby można było pluginy do NB i Eclipse napisać. Ale chwilowo muszę podgonić doktorat, bo zostało mi pół roku. ;)