( asch | 2018. 02. 02., p – 13:37 )

Én is Java fan vagyok, szeretem is, meg sokat foglalkozok is vele, hogy pontosan hogyan működik. De az eszköz ismerete együtt kell hogy járjon a korlátainak ismeretével is. Anélkül olyan, mintha csak a marketinganyagot adnád vissza.

Az való igaz, hogy átlagos throughput szempontjából sokszor ezek a tényezők - ellenőrzések - a legfontosabbak. És egyetértek, hogy az esetek 95%-ában jó trade-off a biztonság érdekében az a kis szorzó. És ne is beszéljünk arról, mennyivel egyszerűbb egy out of bound exception-t debuggolni annál, mint amikor "véletlenszerűen" felülíródnak változóid a program másik felében egy túlcímzés miatt...

Amit viszont nem látsz, vagy nem ismersz be az az, hogy a GC miatti kiszámíthatatlan válaszidő egy csomó projektben bizony probléma. És ezt JVM-en belül egyszerűen nem lehet megoldani. Kis heapre is 50 ms körül lehetsz (régen mértem elismerem), nagy heapre meg óriási laggok vannak. Nekem egyszer kellett ilyen programot írnom, és a vége az lett, hogy a protokoll bonyolult és hibaesélyes részét Java kezelte, az egyszerű de időzítéskritikus részét pedig C küön processzben. Mert másképp nem működött, viszont Java fan vagyok és nem akartam az előnyeit teljesen elveszíteni.

Egy másik programban a sokszor futó loop-on belül cache-elni kellett az objektumokat, így sikerült elérni, hogy egyáltalán ne keletkezzen szemét. De ez már olyan erőfeszítés, hogy akár eleve lehetett volna C stacken foglalt objektumokkal, nem lett volna bonyolultabb. Persze megintcsak a program többi részén lehetett nyerni a Java-val, összességében még pozitív volt a mérleg.

A másik pedig az objektumok memória layoutja, aminek a fontosságát lekicsínyled. Szerintem is denesb rosszul mondta, valóban az int tömb "tömör" ahogy mondod is. De ha mondjuk egy generikus fát szeretnél intekkel, vagy longokkal kulcsolni, ott bizony már külön objektumban tárolódnak a kulcsok. Tehát 4 vagy 8 bájt helyett 8(4)+16(12?)+4 bájton (64 vagy 32 bites JVM függő) ami ráadásul nem is egymás mellett van. Ez már nagyon nem mindegy. Persze írhatsz, vagy használhatsz tömbbel implementált fát, de akkor meg a collection FW előnyeit nem tudod kihasználni végül, mert az összes API-t is primitív típusokra át kell írnod. De ha valami bonyolultabb struktúrát kell tárolnod hatékonyan, akkor marad hogy "kézzel" számolgatsz indexeket a tömbödhöz. Ilyet is csináltam már, működik, de megintcsak sajtreszelős.

A cache használata meg nem vicc. Mivel a programok általában RAM sávszélesség korlátosak, a limitellenőrzés minimális overhead. A tömb limitje tipikusan cache-ben lesz, vagy akár regiszterben, ha a JVM oda tudja tenni. Az ellenőrzés olcsó lesz. De ha egy loop sokszor fut, akkor nagyon nem mindegy, hogy minden adata is cache-ben van-e, vagy minden iterációban ki-be kell rángatni. Ezért mondom, hogy téves azt gondolni, hogy az ellenőrizgetés a sok. Kiélezett helyzetekben sokkal inkább ez lesz a limit. És a JVM memóriaszervezésével könnyen az utóbbiba futhatunk ott, ami C-ben simán benntartható lenne. Ezen sokszoros sebességek múlhatnak. Nem triviális rájönni sem, hogy ez az ok, és szintetikus benchmarkok csak akkor hozzák ki, ha a C tábor tervezi őket :-). Az is igaz, hogy ez már komoly minőségi szint egy C projekt esetén is amikor ezzel számolni kezdenek, de ott legalább meg lehet tenni, JVM esetén meg minimális eszközünk van rá.

Az ideális talán az volna, ha a protokoll ellenőrzés és belső parancsokra fordítás Java volna, a motor meg C-ben lenne megírva. Aminek gyors válaszidő kell, előre compilált lekérdezésekkel fordulhatna a C backendhez, így kikerülné a Javaból adódó esetleges jittert, de mégis nehéz volna támadni.