Tyto benchmarky ukazuji jak se ktery rezim projevi prave na techto peti instrukcich, nic vic. Dle meho laickeho nazoru se tady projevuje to ze -server nedostal moc prostoru aby se poustel do nejakych vetsich akci. U svych aplikaci dosahuju s -server radove lepsich vysledku, takze vim ze to uzitecny je.
Kdyz uz pisu - libilo by se mi kdyby se autor dostal k pokrocilejsim technikam jako inline metod, pouzivani zasobniku pro alokace apod., cetl jsem o tom ruzne po netu ale nevim ktere JVM to pouzivaji, zda je to v oracle JVM atd.
odstavec 1 = presne tak, ale zrovna dneska vubec neslo o porovnani JITu, ale o to, ze ani nejlepsi soucasny JIT proste nedokaze nic udelat se synchronizaci ci volatilnimi atributy. Z posledniho grafu (mozna jsem je mel dat vedle sebe - priste) je videt, jak JIT C2 krasne zoptimalizoval tu prvni metodu (inlining + rozbaleni smycky), ale u dalsich se zasekl :-)
odstavec 2 = ano chystam se na to + asi jeste popis, co dokaze JIT delat v pripade, kdy ma vice synchronizovanych pristupu k nejakemu objektu, to je zajimave.
Server mod samozrejme *neni* k nicemu, ale nekolikrat uz bylo zmineno, jak a ve kterem okamziku zacina davat smysl a kde smysl proste nema = neni to proste stribrna kulka, ktera magicky zachrani a urychli vsechny Javovske aplikace, to ani nahodou.
Pokud se opravdu jedna o kratkodobe bezici aplikaci, tak si sam muzete vyzkouset (staci i zabudovany profiler), ze server mod muze byt horsi, ostatne jednou z nejvetsich prednosti JVM je fakt, ze si muzete vybrat mezi interpretrem, client rezimem, server rezimem, tiered kompilaci a dokonce (ti optimisticteji naladeni :-) si muzou vynutit ihned JIT kompilaci.
A ja se tu snazim propagovat vlastni premysleni nad problemem - uvidite sam, PROC v tomto pripade server rezim neexceluje i duvody proc tomu tak je - proste aplikace bezi tak kratkou dobu, ze se tam projevi pomalost JITu C2 prekladace, on je skutecne pomerne komplikovany.
JVM - pouzivam jak referencni implementaci OpenJDK7, tak i Oracle JDK7 se stejnymi vysledky na stejnem stroji.
Realisté by pak ještě ocenili možnost konkrétní optimalizace při JIT kompilaci vypnout. Některé optimalizace (JRockitu) totiž končí v lepším případě pádem JVM, v horším neuvěřitelnými výjimkami – NoClassDefFoundError pro java.lang.Long nebo UnknownFormatConversionException: Conversion = 's' při volání String.format("%s") …
Bohužel jen v reálně nasazených systémech, na testovacích prostředích se nikdy nepodařilo JRockit donutit, aby tu konkrétní nepovedenou optimalizaci provedl… Nejsou ale obě chyby z jedné verze JRockitu, kvůli té chybě s Long jsme se museli vrátit k předchozí verzi JRockitu, která teď 3× za posledních pět měsíců vytvořila tu chybu se String.format – v jedné konkrétní metodě, při logování vzniklé výjimky UnknownFormatConversionException se String.format("%s") volá znovu, a tam už to projde. Nabízí se samozřejmě možnost přejít na nejnovější verzi – ovšem ty chyby tam mohou být pořád, případně tam mohou být nové.
A neni treba problem v tom, ze se vsechno spousti z main metody? Ze je treba takovyhle micro-benchmark spatne napsany?
Udelal jsem si jiny testy, ktery meri uplne to same (metody test.()), ale merici smycky nejsou v main. Stejny JVM warm-up. SynchronizationTest1 je puvodni z tohoto clanku - ostatni test jsou z neho odvozene.
Zde je kod s vysledky: https://gist.github.com/karl82/6898012
SynchronizationTest1: client Cumulative time for testX(): 51,702,940 ns Cumulative time for testY(): 616,381,691 ns Cumulative time for testZ(): 619,223,672 ns Cumulative time for testV(): 290,305,054 ns server Cumulative time for testX(): 30,222,000 ns Cumulative time for testY(): 595,976,023 ns Cumulative time for testZ(): 611,052,521 ns Cumulative time for testV(): 356,544,757 ns SynchronizationTest2: client Cumulative time for testX(): 52,769,276 ns Cumulative time for testY(): 597,176,176 ns Cumulative time for testZ(): 606,081,219 ns Cumulative time for testV(): 298,170,577 ns server Cumulative time for testX(): 3,193,702 ns Cumulative time for testY(): 148,998,265 ns Cumulative time for testZ(): 601,241,222 ns Cumulative time for testV(): 377,179,224 ns SynchronizationTest3: client Cumulative time for testX(): 52,683,791 ns Cumulative time for testY(): 603,012,115 ns Cumulative time for testZ(): 606,263,366 ns Cumulative time for testV(): 293,399,861 ns server Cumulative time for testX(): 3,345,399 ns Cumulative time for testY(): 152,064,858 ns Cumulative time for testZ(): 151,931,044 ns Cumulative time for testV(): 306,139,188 ns
Udelal JIT C2 lepsi praci? Jaky maji moje testy vypovidaci hodnotu v porovnani s testy v clanku?
Proc uvadite, ze C2 je pomalejsi, kdyz je v kodu warm-up JVM a JITu?
Jasne, zasadni vylepseni je jen v poslednim pripade, coz souvisi s vlastnosti (optimalizace za sebou jdoucich locku stejneho objektu) o niz budu mluvit priste. V pripade SynchronizationTest2 bych rekl, ze by to chtelo vic iteraci, aby se vysledky testY() a testZ() srovnaly, musim se ale podivat na asm.
Porad ale nechapu, kde vidite zasadni problem. Neporovnavame zde C1 s C2, ale veci souvisejici se synchronizaci ne?
U SynchronizationTest3 uz doslo k vyrovnani vysledku.
Ano, resime veci se synchronizaci. Ale jsou uvedeny vysledky z client (C1) a server (C2) mode. Takze porovnani tu je. Kazdy JIT resi veci jinak a mohou vect k eliminaci casti kodu napr. v single thread aplikaci.
Vysledkem je, ze kdo si precte konrektne 5. dil tohoto serialu, tak si odnese poznatek, ze JVM server a C2 jsou pomalejsi pri behu critical sections.
1. Ten micro-benchmark bude asi slouzit jako inspirace pro ostatni k psani benchmarku. Napsat benchmark, aby mel vypovidajici hodnotu je velmi tezke. https://code.google.com/p/caliper/wiki/JavaMicrobenchmarks
2. Provadet benchmark primo z main neni dobre http://www.azulsystems.com/blog/cliff/2011-11-22-what-the-heck-is-osr-and-why-is-it-bad-or-good
3. Vysledky mereni jsou publikovany a nejsou vysvetleny do detailu. Na Java DZone je takovychle mereni a testu spousta a jsou zavadejici.
Pane Tišnovský, chtel bych jeste uvect, ze si vazim Vasi prace a autorske cinnosti!
Ono to fakt vypada na to, ze JIT C2 zjistil, ze sync zde neni potreba, musim se podivat na ten assembler. Poustel jste to asi na x86_64 ze?
Hmm s tim OSR je to skutecne pravda, dalsi vec, o ktere se bude dobre nekdy priste zminit.
S 3) si musim dat asi vetsi pozor, ja to fakt smeroval hlavne k synchronizaci (a proc ma interpret problemy s delsim bajtkodem :-), ale je fakt, ze se daji interpretovat ruzne :-
Prave jsem se spoustel na 32bit JVM i kvuli client modu. Aby byly vysledky porovnatelny.
S tim asm je to urcite dobry napad. Sam jsem nezkoumal bliz ty synchronizace, ale tipuju na biased locking.
Osobne nepovazuju interpret za opravdu dulezity, protoze se "hot" veci, ktere jsou kriticke, velmi rychle prelozi do native code.
Urcite u serverovych aplikaci nebo appek ktere bezi dlouho (u me napriklad Eclipse v podstate cely den) je interpret zanedbatelny.
Problem to trosku je v pripade, kdyz se spousti nejaky jednodussi tool, co bezi treba jen par minut, tam se nekdy pres tech defaultnich 10000 volani (full optimize) nejake metody ani nepreleze, takze se hodne veci jen interpretuje (ne zakladni tridy, samozrejme ty nekdy dojdou k JITovani jeste pred spustenim main :)
Ja jsem to trosku zkousel trasovat (tusim na to byl i priklad u clanku o JVM TI) a tech metod, co se volaji jen jednou a nemaji nejake brutalni smycky, se vola dost - opet problem spis u aplikaci spoustenych na chvilku.