Megjött a natív sebességű kódfuttatás a böngészőkbe

Megjött a natív sebességű kódfuttatás a böngészőkbe
2016-11-02T11:34:37+01:00
2016-11-14T09:51:29+01:00
2022-10-20T03:40:43+02:00
  • Mutasd a teljes hozzászólást!
  • Bar, ahogy emlitettem - igy viszont van plusz koltsege a dolognak, tobb memoria fogyasztas.

    A több memória fogyasztás szintén nem egyértelmű, hogy melyiknél rosszabb mivel a Java tervez vele (2 survivor space egy helyett, viszont van compaction). 
    A C++ viszont nem tervez vele, helyette szenved tőle, mert többet allokál, mint akkor kéne ha lenne benne compaction és pointer redirection. Ergo a C++ program sokkal több memóriát is elpazarolhat emiatt, mint a Java.
    Mutasd a teljes hozzászólást!
  • Kivéve persze azok a Javascript GC-k, ahol a Javascript motor a JVM-en fut, pl. Nashorn és egyéb ilyen JS engine-k.
    Mutasd a teljes hozzászólást!
  • Javaban meg a legkisebb osztalybol letrehozott objektum is a heap-ben jon letre, termeszetes a fragmentacio. (Szerk:) Hacsak azota nem fejlesztettek rajta.

    Valamennyit fejlesztettek, nem sokat és kicsit lutri jellegű, és egy ideig bugos is volt, de van escape analysis alapján stack allokáció már Java-ban (de nem túl régen).

    Tehát ha egy objektum nem menekül el az adott thread-ről (nincs átadva más szálnak, és a hívott metódusok nem tárolják statikusan elérhető kontextusban), és ezt a JIT compiler felismeri, akkor a stacken fogja allokálni, nem a heap-en. A kérdéses rész itt a "JIT compiler felismeri".

    Viszont pont emiatt a fragmentáció miatt nagyon régen kezelik is ezt a fragmentációt, és a lyukak a young gen-ből nagyon gyorsan eltűnnek (minden copy collection-kor, tehát amikor az Eden vagy az aktuális survivor space betelik), az old gen-ből meg full GC-kor.
    Mutasd a teljes hozzászólást!
  • Mutasd a teljes hozzászólást!
  • Igaz, koszi az infot!
    Bar, ahogy emlitettem - igy viszont van plusz koltsege a dolognak, tobb memoria fogyasztas. Viszont nem celom kihozni melyik a jobb - ez hulyeseg lenne - mindegyiknek van megvan a maga alkalmazasi terulete.
    Mutasd a teljes hozzászólást!
  • inkabb hasonlo elvu GC-s javascriptekkel

    A JavaScript GC-k kb. 10 évvel le vannak maradva a Java memóriakezelésénél.
    Mutasd a teljes hozzászólást!
  • Javaban meg a legkisebb osztalybol letrehozott objektum is a heap-ben jon letre, termeszetes a fragmentacio.

    Azért nem annyira természetes az. A fiatal generáció mindig teljesen kompakt, mivel ennek a takarításakor egyszerűen átmásolódnak az életben maradt objektumok egy másik területre, az allokáció pedig mindig a szabad terület elején történik. Fragmentáció csak az idős generációban fordulhat elő, de ott meg ugye sokkal ritkább a felszabadítás, illetve lehetőség van kompaktálásra.

    Szóval a Javában a fragmentáció egy átmeneti probléma, ami GC közben megoldható, míg C++-ban legfeljebb megelőzni lehet a fragmentációt (az általad említett módszerekkel, és/vagy low-fragmentation heap használatával), de a meglevő fragmentációt megszüntetni nem lehet.
    Mutasd a teljes hozzászólást!
  • Egy kérdés: sok Java-ban írt programot használsz azon a gépen?

    Szerencsere nem :D inkabb hasonlo elvu GC-s javascriptekkel teletuzdelt oldalakat es browsert, amelyek kepesek megzabalni a memoriat.

    Ja és mellesleg a legtöbb notebook bővíthető, ha ilyen igényeid vannak...

    Tudom, nekem nem problema, de azert na! Egy telefon memoriajat nem fogod boviteni. Vehetsz uj telefont. Beagyazott hardvert sem lehet csak ugy boviteni. Tehat vannak teruletek ahol meg az ugyfel sem nezi jo szemmel a "memoria pazarlast".

    Nem a desktop/notebook kategóriát kell vizsgálni.

    En ugy altalaban vizsgaltam a dolgot, nem csak adatbazis szemszogbol tehat azt is neztem.

    A Java-t alapvetően továbbra is szerver oldalon használják. Egyébként sajna itt is hajlamosak spórolni a cégek (leginkább azzal hogy blade-eket vesznek, ami egyébként alapvetően hülyeség, ha sok ramra vagy sávszélességre van szükséged), de azért a lehetőség megvan a mai (nem blade) szerverekben akár több terabyte ram elhelyezésére is.

    Tudom ,ezzel egyet is ertek, nem vitatkozok azzal hogy ilyen helyen jo a java es a sok memoria. Csak a legutobbi embedded fejlesztoi konferencian mar megjelentek javascriptes! beagyazott (op.)rendszerek. GC ilyen helyen nekem tul extremnek tunik.

    Ezt légy szíves gondold át még egyszer, mert nagyon hülyeségnek hangzik ha komolyan gondoltad, ugyanis az említett funkcionalitásnak semmi köze a problémához, a probléma ugyanis a kerneltől kapott lap belső töredezettsége, amiről a kernel semmit nem tud, valamint a C++ sem fog minden objektum memória allokációjáért a kernelhez fordulni, annál nagyobb adagokban dolgozik. Ellenkező esetben még több memóriát és CPU ciklust pazarolna azzal, hogy kis objektumokhoz is egy egész lapot kell elkérnie és még context switchelnie is kell.

    Az operációs rendszertől kapsz egy lapot X méretben, legyen mondjuk 4k (teljesen mindegy mekkora).

    Tele allokálod 32 byte-os objektumokkal, majd minden másodikat felszabadítod ezek közül.

    Ha nem írod át a pointereid azon részét ami a cím lapon belüli legalsó 12 bitjét tárolja, akkor nem tudod a 32 byteos lyukakat összeolvasztani.

    Igy van ahogy irod, valoban nem ilyen kicsi allokaciora gondoltam hanem nagyobbra, ugyanis kis allokaciok eseten lehet hasznalni a reserved funkciojat a kontenereknek vagy mas modszert, strategiat amivel az ilyen eseteket meg lehet elozni. Az oprendszer ugy sem fogja lefoglalni a tobblet memoria teruletet (lazy allocation) ha az nagyobb mint 4k de nincs kihasznalva. Illetve c++-ban lehet objektumokat pakolni a stack-re is. Kis meretu allokaciokat pont ezert teszek szivesebben a stack-re.

    Aki erre nem figyel az szepen kap egy - needs work flag-et a munkajara.

    Javaban meg a legkisebb osztalybol letrehozott objektum is a heap-ben jon letre, termeszetes a fragmentacio. (Szerk:) Hacsak azota nem fejlesztettek rajta.
    Mutasd a teljes hozzászólást!
  • Viszont az altalad emlitett (ObjectLayout, StructArray) dolgok arra engednek engem kovetkeztetni, hogy amint letrehozzak ezeket a nyelvi elemeket, onnantol szabad a pálya a SIMD-hez.

    Hát, sajna ez még nem feltétlen adja meg neked az SIMD utasításokat, csak a megfelelő elrendezést. Viszont az elrendezést már jelenleg is el tudod érni heap-en kívül az Unsafe class használatával (amire már jelenleg is van intrinsic támogatás, tehát még olcsóbb is mint egy array elérés mivel nincs index check).

    Ram ár: Néztem eloszor, hogy ott nem 40, hanem csak 30 rongy, viszont ha rárakom az világbajnok áfánkat, akkor kijon, hogy az itteni bolt egy szallitassal egyutt ugyanott lenne.

    A 30-ban is van angol ÁFA (20%), szóval ha rá akarod a magyar ÁFÁ-t tenni akkor vedd le az angolt. A szállítási költség sajna tényleg van, ebből a szempontból London előnyösebb, ide ingyen kihozzák.
    Mutasd a teljes hozzászólást!
  • Azt hozzatennem, hogy en csak olyan dolgokat vallalok be, ahol optimizalni kell, szal emiatt szeretem ezt a temat kulonoskeppen. Es semmikeppen sem eroltetnem olyan helyekre, ahol nem az szamit. Azert egyszeruen nem is fizetnenek amugy sem, szoval minek.

    Tehat ez a structs_of_arrays megkozelites az az már csak L1 cache-nal fontos. Ha meg lehet oldani, hogy olyan formaban kapja a processzor az adatokat, amivel utana mar azonnal tud is szamolni, az sokkal jobb, mintha a szamolasok elott kulott shufflézni kéne az adatokat.

    Egy egyszerubb peldaval szerintem jobban atjon: tegyuk fel, hogy van egy szurkearnyalatos kep, amin egy 16x16-os csuszoablakot kell vegigvinni es az ablak átlagértékét kell visszaadni. Ez egy nem tokeletes, de egyszeru alulatereszto szuro 2D-ben.

    Alegegyszerubb megoldas az lenne, hogy:
    - ciklus minden pixelre, azon belul ciklus a 16*16-os ablakra es benne az akkumulalas, vegen meg ott az eredmeny 8bit, ki lehet irni az eredmeny bitmapba.

    Mivel a kernel szimetrikus, raadasul mind a 16x16 elemének a sulya az avg fuggveny miatt megegyezik, emiatt a fenti muvelet oszehozhato egy vizszintesen mozgó 16x1-es csuszoablak es egy fuggolegesen mozgo 1x16 meretu csuszoablak alkalmazasaval. Itt mar 1/8 annyi muvelet kell csak az agesz feladathoz.

    Es akkor itt johet be az a legalso szint, amirol beszeltem: SSE-vel lehetoseg van 16 byte-ot beolvasni es ezeket atkonvertalni 16 word-dé, amikrol aztan szepen lehet csinalni a mozgó összeget. Igen ám, de ez csak a fuggolegesen lefele iranyba mukodik, mert csak ott tud 16 pixelt beolvasni, hogy az a 16 pixel pont egymas mellett legyen a memoriaban. Tehat az egyik irany kb 8x olyan gyors lett de a masik még nem.

    Itt johet akkor az emlitett 'kinlodas', a fuggoleges iranyu muveletet megelozi egy 45 fokos fôátlóra  való tukrozes, amit egy 16x16-os blokkra kb 60 komplikalt utasitassal, de meg lehet csinalni. Es két ilyen tukrozes+csuszoatlag szamitas muvelet egymas utani vegrehajtasa megcsinalja a 2D filterezest. Ami a kinlódás benne csak az, hogy ez a 16x16 diagonal flip igencsak egy agyf*sz muveletsor, viszont a segitsegevel a teljes fele masodik fele is 8x gyorsabb lehetett.

    Szoval az SSE nativ adattipusai ezek: char[16], short[8], int[4], stb. Ha pedig az adat nem ilyen formaban jon, akkor vagy a korulotte lévo dolgokon valtoztatunk, vagy belul irunk ra egy konvertert, ami ilyenekbe konvertál. Ha a konvertalas koltsege kisebb, mint a vektor tipusok elonye, akkor már megérte.

    Pl. egy RGB[16] -> R[16], G[16], B[16] konverter az eleg koltseges dolog tud lenni egy 10 eves processzoron, de char[16]-on alkalmazhato add,sub,cmp,min,max,avg,shr,shl,bitwise fuggvenyeken ezt a koltseget sokszorosan vissza lehet nyerni. Es annyit tettunk, hogy array_of_struct-bol atmentunk structs_of_array-ba. Ha pedig ezt ujabb proszesszorra kell megcsinalni, amiben már van char[16]->char[16] permute, akkor ez a konverter 2-3x gyorsabb is lehetne es akkor plane megeri bibelodni vele.

    Nem tudom C++-ban hogy van ez pontosan

    Itt inkabb ugy fogalmaznék, hogy ez mar nem C++-ban, nem is high level nativ nyelvben van igy, hanem az x86/IA-32/amd64 (kinek hogy tetszik) processzor architekturakon van igy.
    Engem nem is nagyon erdekel, hogy C++ vagy Delphi, mert mindkettobol kenyelmesen el lehet erni az alatta levo procit. Java-nal viszont mas a helyzet, mert ott ilyet nem lehet. Viszont az altalad emlitett (ObjectLayout, StructArray) dolgok arra engednek engem kovetkeztetni, hogy amint letrehozzak ezeket a nyelvi elemeket, onnantol szabad a pálya a SIMD-hez. Persze az olyan helyeken, ahol elkerulhetetlen a shuffling ott még mindig az asm kell (vagy intrinsic, ha ugy szebb).

    Ram ár: Néztem eloszor, hogy ott nem 40, hanem csak 30 rongy, viszont ha rárakom az világbajnok áfánkat, akkor kijon, hogy az itteni bolt egy szallitassal egyutt ugyanott lenne.
    Amugy nem azt mondom, hogy egetveroen draga, hanem csak hogy nem szeretem a bloatware-eket, de ebben asszem egyetertunk. :D

    (A 16MB-os idoszakban volt asszem szornyen draga a ram, de profibb kepszerkeszteshez akkoriban teljesen jogosan nem lehetett kihagyni.)
    Mutasd a teljes hozzászólást!
  • Nem tudom az otthoni árakat, én Amazon UK-n vettem, és őszintén szólva nem volt egy elvadult ár (Corsair), bár igaz akkor egész olcsók voltak (30-40% árengedmény nem ritka RAM-oknál az Amazon UK-n). A Lenovo shopos áraknak kb. a fele volt vagy még olcsóbb.

    A legujabb jatekok repulnek rajta, a bloatware jellegu nem tul hatekony szoftvereken viszont ugysem lehet segiteni, mert ott céges szinten sokkal olcsóbb még a legdragabb ECCs ram is, minthogy az évek óta bövített szoftverböszmeségeket ujrairassák hatékonyabbra.

    Igen, elég olcsó, feltéve ha nem blade-eket használnak amit elég korlátozottan lehet bővíteni.

    Itt mar a arra_of_structs szerkezetbol ha nem is teljesen, de reszben at kell allni struct_of_arrays-ra.

    Nem tudom C++-ban hogy van ez pontosan, overhead van a structokon is? Ha nincs akkor nem látom feltétlen miben jobb a structs_of_array az array_of_structs-nál, ugyanis az array of structs az iteráció lépéseivel az egész struct-ot tudja neked fetchelni. Vagy annyira széles a struct-od, hogy 1 prefetcher nem bír lépést tartani vele (a struct csak kis részét használod egyszerre)?

    Ez utobbira tok jo lenne, ha lenne valami high level support nyelvi szinten, de mivel nincs, ezert kinlodni kell vele

    Java-ban Java 10 körülre várható (ObjectLayout, StructArray), hamarabb nem sanszos. Workaroundnak ott a SBE Martin Thompsontól.

    Sokszor persze lehet ez is megoldas, de ha a bloatware faktoron van mit csokkenteni, akkor az az út nekem szemely szerint sokkal jobban tetszik.

    A bloatwaren mindenképp érdemes csökkenteni, ebben egyetértünk, és értelmesen kell hozzáállni a memória használathoz, már csak az adatszerkezetek CPU-barátsága miatt is.

    Ugyanakkor a gondolkodás nélküli sokkal "jobb a C++-ban a destruktor" is hasonlóképpen nagyon sokszor gondolkodás nélkül van rávágva, mivel sokszor nincs is rá szükség, viszont lassít.
    Mutasd a teljes hozzászólást!
  • Hát most megneztem, hogy mennyire olcso, de nekem inkabb eleg lesz a 8GB, ami van :D
    A-Data XPG V2 Golden 16GB (2x8GB) 2400MHz DDR3 memória Kit CL11 - eBolt Műszaki Áruház

    A legujabb jatekok repulnek rajta, a bloatware jellegu nem tul hatekony szoftvereken viszont ugysem lehet segiteni, mert ott céges szinten sokkal olcsóbb még a legdragabb ECCs ram is, minthogy az évek óta bövített szoftverböszmeségeket ujrairassák hatékonyabbra.

    Ahhoz hogy egy SW hatekony legyen, egyszerre kene odafigyelni minel tobb szintre:
    - hosszabb tavon ferjunk bele a ram-ba!
    - pár milliseces idôtartomanyban férjünk bele az L3/L2 cacheba! Ha nem akkor erdemes lehet menet kozben tomoriteni is, ugyanis itt még a cpu time szinte ingyen van a lassu ramhoz kepest.
    - es vegul ott az L1 cache, ami pont ugyanolyan gyors, mint a cpu. De a kihasznalasahoz mar at kell térni SIMD-re es - guess what! - ezt megint csak az adatok helyének megvarialasaval lehet csak elérni. Itt mar a arra_of_structs szerkezetbol ha nem is teljesen, de reszben at kell allni struct_of_arrays-ra. Ez utobbira tok jo lenne, ha lenne valami high level support nyelvi szinten, de mivel nincs, ezert kinlodni kell vele, vagy marad a "vegyunk mégtöbb memoriát" wild card.

    Ezt csak annak apropojan irom, hogy már nem eloszor hallom az "olcsó ram, mint megoldás" szoveget :D Sokszor persze lehet ez is megoldas, de ha a bloatware faktoron van mit csokkenteni, akkor az az út nekem szemely szerint sokkal jobban tetszik.
    Mutasd a teljes hozzászólást!
  • ont ezert mindig ebbol van a legkevesebb, mert kevesbe optimalizalnak a szoftver gyartok :D 8GB van a gepemben de keves, bongeszo lefoglalja (lasd alabb miert), 16 kellene. Vagy lasd lenovo ujjlenyomat olvaso c#-ban irt drajvere, 1 het utan mar 500MB-ot hasznalt el.

    Egy kérdés: sok Java-ban írt programot használsz azon a gépen? (Ja és mellesleg a legtöbb notebook bővíthető, ha ilyen igényeid vannak... általában eggyel nagyobb ramot még bele lehet rakni... nekem is 28GB-ig bővíthető a notebookom... csak az utolsó 8-hoz szét kéne kicsit szedni hogy hozzáférjek a slotokhoz). Nem a desktop/notebook kategóriát kell vizsgálni.

    A Java-t alapvetően továbbra is szerver oldalon használják. Egyébként sajna itt is hajlamosak spórolni a cégek (leginkább azzal hogy blade-eket vesznek, ami egyébként alapvetően hülyeség, ha sok ramra vagy sávszélességre van szükséged), de azért a lehetőség megvan a mai (nem blade) szerverekben akár több terabyte ram elhelyezésére is.

    Ellenben a memoriakezeles eltero szemlelete miatt javaban sokkal tobb memoriara van szukseg. Ez lehet 2-3 szoros, extrem esetben 10x-es.

    A 2-3-szoros az reálisnak hangzik szövegfeldolgozásnál. A 10x-es az nekem már túl extrémnek hangzik, ott egyszerűen valószínűleg szarul van tervezve a rendszer, ami viszont nem a Java miatt van, ugyanaz a megoldás más nyelvvel megvalósítva is valószínűleg többet foglalna mint szükséges.

    Na, ezt nem talalom igaznak. Az egesz memoriaterulet ami virtualis memoriakent hasznalva van, rettenetesen toredezett. De egyaltalan nem problema mert hardveresen kezelve van a szituacio. Lasd: https://events.linuxfoundation.org/sites/events/files/slides/elc_201.. Az MMU es TLB gondoskodik arrol hogy a 4k-s lapok (PC-n, linux alatt) folyamatosnak latszodjanak az elinditott program szemszogebol. Igy c++ szemszogbol sem lenyeges (altalaban) ha nem folyamatosak a lefoglalt memoria teruletek.

    Ezt légy szíves gondold át még egyszer, mert nagyon hülyeségnek hangzik ha komolyan gondoltad, ugyanis az említett funkcionalitásnak semmi köze a problémához, a probléma ugyanis a kerneltől kapott lap belső töredezettsége, amiről a kernel semmit nem tud, valamint a C++ sem fog minden objektum memória allokációjáért a kernelhez fordulni, annál nagyobb adagokban dolgozik. Ellenkező esetben még több memóriát és CPU ciklust pazarolna azzal, hogy kis objektumokhoz is egy egész lapot kell elkérnie és még context switchelnie is kell.

    Az operációs rendszertől kapsz egy lapot X méretben, legyen mondjuk 4k (teljesen mindegy mekkora).

    Tele allokálod 32 byte-os objektumokkal, majd minden másodikat felszabadítod ezek közül.

    Ha nem írod át a pointereid azon részét ami a cím lapon belüli legalsó 12 bitjét tárolja, akkor nem tudod a 32 byteos lyukakat összeolvasztani.  

    Tehát van 4k-d lefoglalva aminek a hasznos foglaltsága 2k, és elméletileg van 2k szabad helyed. Mekkora lesz a legnagyobb objektum amit allokálni tudsz mielőtt kérned kellene egy újabb 4k-s lapot? 32 byte, mivel ennyi a legnagyobb összefüggő szabad terület a 4k-s lapodon. Tehát szeretnél egy 33 byteos objektumot allokálni, hiába van 2k-d szabadon, mégis kérned kell egy újabb lapot.

    A GC-k kontextusában ezt hívják fragmentációnak, és nem arról beszéltem amit az operációs rendszer kezel ahol lapok az egységek, azt a GC nem látja és nem foglalkozik vele.

    Java-ban ez a probléma meg van oldva.

    Ha a C++-ban nincs, akkor igenis a C++ oldalán jelentkezik a probléma, mert pazarolja a memóriát, és gyakrabban kell a kernelhez fordulnia, tehát átlagosan lassabb lesz az allokációja is.
    Ha C++-ban is meg van oldva, akkor mi különbözteti meg a C++ memória menedzserét egy GC mechanizmustól? Csak az esetleges ütemezési garanciák az objektumok eltakarítására, ami viszont az alkalmazást kell lassítsa, ha a felszabadítás művelet visszatérte előtt garantálni akarod a destruktor lefutását.
    Mutasd a teljes hozzászólást!
  • Csak nehany megjegyzes:

    Egyrészt 2016-ot írunk. A memória az egyik legolcsóbb erőforrás

    Pont ezert mindig ebbol van a legkevesebb, mert kevesbe optimalizalnak a szoftver gyartok :D 8GB van a gepemben de keves, bongeszo lefoglalja (lasd alabb miert), 16 kellene. Vagy lasd lenovo ujjlenyomat olvaso c#-ban irt drajvere, 1 het utan mar 500MB-ot hasznalt el.

    De ugyanez az előny a C++ alapú kódra is igaz, mivel ha elegendő memória áll rendelkezésre már a kerneltől allokálva, akkor a C++ kód is gyorsabban fog memóriát allokálni ahhoz képest, mintha a kerneltől kellene további területeket kérjen. Minél ritkábban kell a kernelhez forduljon, annál gyorsabb átlagosan a C++ kód is.

    Ez minden program sebessegere igaz, ha az oprendszer lapozofajllal van elfoglalva akkor lassan fut. Ellenben a memoriakezeles eltero szemlelete miatt javaban sokkal tobb memoriara van szukseg. Ez lehet 2-3 szoros, extrem esetben 10x-es. A memoria kezeles fuggo benchmarkok sokszor azert is mutatnak jobb eredmenyt javaban, mert a gc nem hivodik meg azonnal ha mar nincs szukseg egy memoria teruletre hanem csak bent tartja a memoriaban, hatranya ennek a szemleletnek h sokkal tobb memoriara van szukseg, ami bizonyos helyzetekben elfogadhato, bizonyos helyzetekben kevesbe. Persze c++-ban is lehet irni memory poolt ahol nem szabaditjak fel azonnal a memoriat... 

    Ezen kívül, ha a C++ kód nem alkalmaz valamilyen megoldást arra, hogy a saját memória területe ne fragmentálódjon, akkor sokkal kevésbé fogja tudni kihasználni az allokált memóriát, másrészt gyakrabban fog a kernelhez fordulni további memóriáért mint a Java.


    Na, ezt nem talalom igaznak. Az egesz memoriaterulet ami virtualis memoriakent hasznalva van, rettenetesen toredezett. De egyaltalan nem problema mert hardveresen kezelve van a szituacio. Lasd: https://events.linuxfoundation.org/sites/events/files/slides/elc_201.. Az MMU es TLB gondoskodik arrol hogy a 4k-s lapok (PC-n, linux alatt) folyamatosnak latszodjanak az elinditott program szemszogebol. Igy c++ szemszogbol sem lenyeges (altalaban) ha nem folyamatosak a lefoglalt memoria teruletek. Bar van arra lehetoseg h platform fuggo megoldassal folyamatos terulet legyen lefoglalva, ha kell.

    Ha a javaban van egy plusz reteg, ahol folyamatosnak kell lennie a memoria allokacionak - ahogy LCoder irta - akkor ez a hatrany a java oldalan jelentkezik. Csak azert mutatja a benchmark gyorsabbnak a memoria kezelest mert - ahogy fentebb emlitettem - GC nem fogja egybol kidobni a nem hasznalt lapokat, inkabb bent tartja egy ideig. Tehat nem okoz kernel hivasokat addig a program csak noveli az altala lefoglalt memoriateruletet.

    Ezt nem abbol a szemszogbol irtam hogy kinek nagyobb a... es c++ mindenhol es forever etc. (Termeszetesen az uzleti, db vilagban olcsobb vasat tenni a szoftver ala mint kinlodni a memoria kezelesi problemakkal - ami nem lenne ha kedves fejlesztok betartananak par nagyon egyszeru iranyelvet, de nem teszik :) )
    Mutasd a teljes hozzászólást!
  • Az alap new/malloc implementációk nem arra vannak optimalizálva, mint a JAVA GC. Viszont a fregmentáció kezelhető, ha állandó méretű objektumokról beszélünk. Márpedig ha tudjuk, hogy egy adott osztályból nagyon sok kis objektumot fogunk dinamikusan létrehozni és törölni, ilyenkor egy dedikált memory pool használata megoldja a fregmentációt, az egyéb esetekben meg ott az alap new..

    Ez egy speciális eset, amit szerintem hasznos lenne Java-ban a fejlesztő számára is eléhetővé tenni (és nem csak a kis méretű objektumok miatt, hanem a memória terület explicite szálhoz/core-hoz rendelése céljából, ami kényelmesebbé tenné NUMA-optimalizált kód írását), de a kérdés alapvetően az általános esetben összehasonlítás volt szerintem. De ha akarod, ez Java-ban is megvalósítható, csak nem objektumokon keresztül (lehet heapen kívül memóriát allokálni Java-ban is a legtöbb JVM-en).

    Viszont nem látom miért kellene a problémát ismert objektum méretekre szűkíteni, általános összehasonításról volt eddig szó.


    Azt én is írtam, hogy nyilvánvalóan lehetséges ugyanolyan gyors kódot írni C++-ban mint Java-ban, mivel minden gépi kódú kód előállítható C++-ból. Szóval az így feltett kérdésen felesleges vitatkozni.

    Csak azt állítottam és az az iparági konszenzus, hogy nem agyonoptimalizált (pl. közepes képességű programozó (aki már hülye hibákat nem csinál) által írt) kód esetén (a Java a C++-al jellemzően összemérhető teljesítménnyel fut... bizonyos dolgokban ez jobb, bizonyos dolgokban a másik).

    A lényeges különbségek ott vannak, ahol a Java architekturális különbségei okozzák a problémát, pl. - a matematikai műveletek lassúak,
    - a String Java 8-al bezárólag 2 byte / karakter szemben a C++ tipikus 1 byte per karakter költségével szemben, ami azt jelenti, hogy tipikus szövegek (ami nem használ spéci karaktereket) esetén a Java fele olyan lassan tudja feldolgozni a szöveget a dupla adatmennyiség miatt.
    - tipikusan a karakterkészlet konverziók ugyanezen problémák miatt (is) Java-ban lassúak
    - jelenleg nyilvánosan elérhető JVM-ekkel még nem lehet kellően szabályozni az objektumok memóriában való elrendezését (pl. nem tudsz struct-okból álló tömböt szimulálni Java objektumokkal jelenleg), ami komplikáltabbá teszi az igazán CPU-barát adatszerkezetek írását, de akkor sem lehetetlen

    De tipikusan a memóriakezelés és a többszálúság területén a Java nem igazán van elmaradva, a C++ nem számottevően gyorsabb, vagy egyenesen lassabb.
    Mutasd a teljes hozzászólást!
  • "Tud az allocator library fejlesztője versenyezni az Oracle és a többi JVM fejlesztő erre a célra fordított erőforrásaival"
    Nem feltétlenül kell:
    Jónéhány átgondolandó pont:
    What backs up the claim that C++ can be faster than a JVM or CLR with JIT?

    Vannak speckó allocatorok, pl tcmalloc (google)

    Az alap new/malloc implementációk nem arra vannak optimalizálva, mint a JAVA GC. Viszont a fregmentáció kezelhető, ha állandó méretű objektumokról beszélünk. Márpedig ha tudjuk, hogy egy adott osztályból nagyon sok kis objektumot fogunk dinamikusan létrehozni és törölni, ilyenkor egy dedikált memory pool használata megoldja a fregmentációt, az egyéb esetekben meg ott az alap new..
    Mutasd a teljes hozzászólást!
  • De persze az OOM-hez az kell, hogy a heap már max. méreten legyen, ha a heap még növelhető, akkor tényleg nem indokolt a full GC.

    De, a full GC indokolt lehet azért is, mert a fragmentáció miatt már nincs elég nagy összefüggő terület az old gen-ben. És old gen növelés előtt lemegy egy full gc. Már ahol van full gc

    Miért is? Ha a heap manager nem is tárolja magának a szabad bájtok számát, akkor is csak végig kell szaladnia a szabad blokkokon és már ki is számolta a szabad memória mennyiségét.

    Nem az a gond, hogy nem tudja mennyi a szabad memória. Az csak egy vagy két számláló karbantartása. A gond az, hogy attól hogy van 10 mega szabadon, attól még nem biztos hogy tudsz egy 2 megás tömböt allokálni, ha a 10 mega az 10 db 1 megás lyukban van.

    Ebben az esetben vagy defragmentálsz, ami után a referenciáid elmozogtak, tehát nem tudom C++-ban ez mennyire reális, vagy plusz lapot kérsz a kerneltől, hiába van darabra elég szabad byte.
    A Java ebben a helyzetben defragmentál, tehát ritkábban megy a kernelhez, mintha nem defragmentálna.
    Mutasd a teljes hozzászólást!
  • Na ez viszont nagyon régen nem így van. Már 10 éve se így volt.

    OK, jogos, erre rosszul emlékeztem. Azzal kevertem össze, hogy OutOfMemoryError dobása előtt köteles a JVM egy full GC-t csinálni. De persze az OOM-hez az kell, hogy a heap már max. méreten legyen, ha a heap még növelhető, akkor tényleg nem indokolt a full GC.

    Ez sem igaz feltétlenül, mivel ez csak akkor igaz, ha a szabad terület egy összefüggő blokkban van.

    Miért is? Ha a heap manager nem is tárolja magának a szabad bájtok számát, akkor is csak végig kell szaladnia a szabad blokkokon és már ki is számolta a szabad memória mennyiségét.

    Mondjuk az jogos, hogy ahhoz a döntéshez, hogy kell-e az OS-től még memóriát kérni, nem kell megszámolni a szabad blokkok összméretét. Csak azt kell eldönteni, van-e elegendően nagy szabad blokk, vagy nincs.
    Mutasd a teljes hozzászólást!
  • A fő különbség, hogy a Java előbb futtat egy full GC-t, és csak akkor szól az OS-nek, ha a GC nem szabadított fel elég memóriát, 

    Na ez viszont nagyon régen nem így van. Már 10 éve se így volt.

    A generational collectorokat futtato JVM nagyon ritkán futtat full GC-t, és ez kb. 1.4.2 óta minden Sun/Oracle/OpenJDK/stb. JVM-re igaz (Hotspot/Zing/Jrockit). A korábban emlegett cikkben használt Jikes-re viszont nem tudom... de annak a fejlesztése emlékeim szerint 1.3 körül meg is rekedt...

    Csak akkor fut le full GC:

    1. Az old gen-be kell allokálni másolni egy objektumot mert vagy túl nagy, vagy mert a young gen-ből kiöregedett.

    2. És emellett
    - vagy az old gen-ben nem tudsz összefüggő területet találni ahová azokat az objektumokat írhatod
    - vagy az old gen foglaltsága az occupancy threshold (%) felett van

    Az első eset önmagában nagyon ritka, mivel a kiöregedés csak a hosszú életű objektumokra jellemző, a rövid ideig használt objektumok általában a copy collection-ök alatt már felszabadítódnak.

    A második eset ezen belül szintén ritka, mivel a JVM a fragmentációt feltakarítja minden Full GC-kor, tehát az kell hozzá hogy nagyobb területet kelljen írni az old gen-be, mint amennyi a szabad terület minusz a legutóbbi full GC óta elhalt oldgen beli objektumok mérete.

    Még üzleti szférában (kereskedési rendszerek) is jellemzően be lehet konfigurálni úgy a JVM-et természetesen egy megfelelően megírt alkalmazás esetén, hogy a Full GC-t el lehet annyira odázni, hogy ne jöjjön akkor amikor gondot okoz, helyette üresjáratban explicite kiváltanak egyet, vagy annyira el lehet odázni, hogy a hetente kéthetente bekövetkező újraindításig nem következik be egyáltalán.

    A G1 emellett még azt is megváltoztatja, hogy a Full GC sem az egész old gen-t takarítja, hanem csak egy részét, ergo a Full GC okozta pause rövidebb.

    A C4 esetén pedig nincs pause... viszont esetleg az alkalmazás szál is beszáll a takarításba, ha nagyon kevés szabad memória van a heapben.

    míg a C++ egyből ki tudja számolni, mennyit nem allokált még az OS-től kapott memóriából.

    Ez sem igaz feltétlenül, mivel ez csak akkor igaz, ha a szabad terület egy összefüggő blokkban van. Ami viszont már egy memória menedzsert feltételez ami a GC-vel összemérhető, ergo egyik GC-t akarod a másikkal összehasonlítani, aminek szintén van értelme, de ugyanakkor megint igazak rá a tesztben írt eredmények, miszerint a JVM gyorsabb.

    Ha viszont nem defragmentálja a lyukakat a C++, akkor viszont gyakrabban fog a kernelhez visszamenni mint a Java, mert ha nem talál elég nagy lyukat, akkor újabb memória terület kell neki, míg Java esetén ez nem feltétlenül van így, és a Java elég erősen arra van optimalizálva hogy erre ne legyen szükség.
    Mutasd a teljes hozzászólást!
  • A C++-os allokációkor se történik általában OS-hez történő lenyúlás. Pontosabban pont ugyanakkor történik, mint Java esetében: ha az eddig az OS-től kapott memóriába nem fér be az újonnan allokált objektum. A fő különbség, hogy a Java előbb futtat egy full GC-t, és csak akkor szól az OS-nek, ha a GC nem szabadított fel elég memóriát, míg a C++ egyből ki tudja számolni, mennyit nem allokált még az OS-től kapott memóriából.
    Mutasd a teljes hozzászólást!
  • Szerintem körtét hasonlítasz almával. Az OS-től a memória megszerzése pont annyira időigényes C++-ban, ,mint JAVA-ban. A különbség az, hogy a GC magától lefoglal nagy szelet memóriát, majd azt menedzseli magának és ezért nem történik JAVA-s allokációkor OS-hez történő lenyúlás.
    Ugyanez a funkcionalitás megvalósítható C++ alatt is pl. 'allocator'-ok segítségével.

    Nem állítottam, hogy C++-ban nem lehetséges, és nem én hasonlítottam össze, hanem csak egy kérdésre próbáltam visszaidézni egy évekkel ezelőtt olvasott cikk tartalmát, ami konkrétan arra is tartalmazott egy állítást, miszerint az objektum allokációt annyira kioptimalizálták, hogy a tipikus eset gyorsabb volt mint a C++ standard library-k hasonló művelete.

    Az érdekes kérdés egyébként ekkor nem az, hogy a new művelet milyen gyors, hanem hogy 
    - a felszabadítás milyen lassú és melyik szálon történik, és több szálról történő felszabadítás műveleteknek kell-e egy közös különálló szállal kommunikálnia (ami pl. sokkal drágább mintha minden szál magának menedzsel egy saját memória területet)
    - a memória menedzser (pl. az általad említett allocator-ok) biztosítja-e azt, hogy a felszabadított objektumok által okozott lyukak nem fragmentálják a memória menedzser üres területét. Ha fragmentálják, akkor gyakrabban kell a kernel-hez visszamenni újabb memóriáért (ami átlagosan jelentősen lassít a context switchek miatt), mert a lyukakba nem tudsz a lyuk méreténél nagyobb objektumot allokálni.
    - mennyire van kioptimalizálva az allocator-ok kódja? A JVM-ek fejlesztői rengeteg erőforrást beleöltek és ölnek jelenleg is, hogy ezt optimalizálják. Tud az allocator library fejlesztője versenyezni az Oracle és a többi JVM fejlesztő erre a célra fordított erőforrásaival és tud-e olyan jó implementációt írni?
    Mutasd a teljes hozzászólást!
  • Szerintem körtét hasonlítasz almával. Az OS-től a memória megszerzése pont annyira időigényes C++-ban, ,mint JAVA-ban. A különbség az, hogy a GC magától lefoglal nagy szelet memóriát, majd azt menedzseli magának és ezért nem történik JAVA-s allokációkor OS-hez történő lenyúlás.
    Ugyanez a funkcionalitás megvalósítható C++ alatt is pl. 'allocator'-ok segítségével.
    Mutasd a teljes hozzászólást!
  • Igazából én csak arra vagyok kíváncsi, mint ahogy a korábban linkelt tanulmányban is szerepelt, hogy konkrétan mennyivel több memóra esetén éri be a GC-t vagy mennyivel lassabb ez alatt.

    Ahogy az angolok mondjak: how long is a piece of string?

    Milyen feladat, hány szálat adsz neki, hány CPU core-od van, ebből hány fut azonos socketen?

    Sokkal egyszerűbb azt vizsgálni, mit tudsz tenni azért, hogy a Java programot ne zavarja a GC (értsd, ne fusson folyton Full GC).

    Erre pedig több megoldás is van, ami ugyanoda vezethető vissza:

    1. Ne allokálj feleslegesen memóriát, amennyit allokálsz az férjen be a young gen-be, és ne allokálj gyorsabban mint ahogy a háttérben futó parallel collector szálak fel tudnak dolgozni. Ez mind olyan tanács, ami a C++ programra ugyanúgy igaz, eltekintve attól, hogy ott esetleg nem háttérben futó szálon történik az objektum felszabadítása. Ha belegondolsz egyébként, a GC-s rendszerek ebből a szempontból kedvezőbbek, mivel elveszik az alkalmazás száltól a feladat egy részét.

    2. Ha mindenáron sok memóriát akarsz hosszú távon használni, akkor allokáld akkora darabokban, hogy rögtön az old gen-be kerüljön, és a maradék allokációd pedig ne váltson ki full GC-t. Ekkor méretezheted kisebbre a Young gen-t (figyelembe véve hogy kisebb young gen gyakoribb copy collection-el jár), és máris kevesebb memória overhead-ed van.

    Az ötszörös szorzó az szerintem már 10 éve se volt igaz. Ha eléred, hogy az old gen-be bekerüljön a munkaterületed, viszont utána Full GC nélkül tudsz működni, akkor JVM opciókkal szabályozható hogy az old gen hányadrésze a young gen, és ez arányaiban tetszőlegesen kisebb lehet az old gen-nél, és a full GC nem zavar be.

    Hovatovább, ha a C++-os memória memória menedzser nem biztosítja a fragmentáció elkerülését, akkor lehetséges hogy a Java alakú kód memória overheadje kevesebb lesz mint a C++ alapú kódé a fragmentáció miatt elpazarolt terület miatt. Sajnos nagyon nem vagyok naprakész C++-ból, ezért nem tudom megmondani, az hogy működik, de a befolyásoló tényezők (egy részét) elmondtam.

    Minden szituáció más és más, még ökölszabály sincs.

    Az az ötszörös szorzó már csak ezért is egy hatalmas hülyeségnek tűnik, meg azért is, mert nem egy elterjedt üzleti JVMet vizsgált, hanem egy kutató realtime JVM-et ami alatt cserélgettek bizonyos algoritmusokat, ergo esélyük sem volt valós tényezőkre optimalizálni az implementációt, viszont megkapták a realtime implementáció további overheadjét. Nem véletlen, hogy a mai napig nem használnak tudtommal sehol hard real-time JVM-et. Egyszerűen nem éri meg, a soft real-time JVM-ek sokkal gyorsabbak és a full GC-k jelentős része elkerülhető/minimalizálható.
    Mutasd a teljes hozzászólást!
  • Igazából én csak arra vagyok kíváncsi, mint ahogy a korábban linkelt tanulmányban is szerepelt, hogy konkrétan mennyivel több memóra esetén éri be a GC-t vagy mennyivel lassabb ez alatt.
    Ez meg nyilván nem derült ki abból.
    Mutasd a teljes hozzászólást!
  • Emellett ugyanúgy lehet az is hogy megdobták bőven memóriával a dolgot, ami mint tudjuk jótékony hatással van a GC-s program sebességére.

    Tekintve, hogy nem a Full GC sebességét mérték, hanem a tipikus use case-ek teljesítményét, így a Full GC önmagában csak egy teszt a sok közül.

    Viszont a memória allokáció sebességét a sok memória csak kevéssé befolyásolja, ha elég nagy a Young generation, akkor mindegy hogy az Old mekkora. Az elég nagy itt azt jelenti, hogy ne teljen be az Eden túl gyakran.

    Egyébként a GC-s program sebessége mint fogalom is szubjektív? Hol méred? Üzleti szálon? CPU-n?

    Alapvetően az a véleményem a kérdésedről, hogy nem azokat a szempontokat akarod figyelembe venni, amelyek költségre lefordíthatóak, sőt egy olyan érzésem is van, miszerint bele se gondoltál a feltett kérdés hogy viszonyul a C++-hoz, ehelyett inkább kötekedni akarsz. Bocs, ha nem így van, nekem ez így jött le.

    Egyrészt 2016-ot írunk. A memória az egyik legolcsóbb erőforrás, tehát még ha egyoldalú is lenne a sok memória által nyújtott előny, akkor is kit érdekel.

    De ugyanez az előny a C++ alapú kódra is igaz, mivel ha elegendő memória áll rendelkezésre már a kerneltől allokálva, akkor a C++ kód is gyorsabban fog memóriát allokálni ahhoz képest, mintha a kerneltől kellene további területeket kérjen. Minél ritkábban kell a kernelhez forduljon, annál gyorsabb átlagosan a C++ kód is.

    Ezen kívül, ha a C++ kód nem alkalmaz valamilyen megoldást arra, hogy a saját memória területe ne fragmentálódjon, akkor sokkal kevésbé fogja tudni kihasználni az allokált memóriát, másrészt gyakrabban fog a kernelhez fordulni további memóriáért mint a Java.

    A GC legtöbbször a háttérben, szabad core-okon fut... ergo elég kevés vizet zavar. A gondot csak akkor okozza, ha az Old gen betelik és emiatt az összes szálat meg kell állítani (implementációtól függően nullaszor, egyszer, kétszer, és ha igen, mennyi időre).

    Természetesen egyetlen összehasonlítás sem arról szól, hogy a leggyorsabb C++-ban megírható megoldást hasonlítsa össze a leggyorsabb Java-ban megírható megoldással, mivel ennek semmi értelme, mivel minden a JIT kompiler által végül kiköpött natív kód előállítható C++-ból, ezért a leggyorsabb C++ kód legalább olyan gyors mint a leggyorsabb Java kód.

    Az értelmes összehasonlítás a közepes/senior programozók által előállított Java és C++ programok értelmesen bekonfigurált futtatásának összehasonlítása azonos algoritmusra.
    Mutasd a teljes hozzászólást!
  • Mondjuk egy ember (akár elfogult) véleményénél szerintem többet ér egy tudományosan kivitelezett teszt.

    Mint írtam az Oracle-nél elvégzett tesztek eredményeit közölte.
    Mutasd a teljes hozzászólást!
  • hogy ért a dologhoz, amellett, hogy egy ideig az Oracle-nél ő alá (is) tartoztak ezek a dolgok, így azt hiszem nyugodtan el lehet fogadni a véleményét a Java teljesítményéről,

    Mondjuk egy ember (akár elfogult) véleményénél szerintem többet ér egy tudományosan kivitelezett teszt.

    Emellett ugyanúgy lehet az is hogy megdobták bőven memóriával a dolgot, ami mint tudjuk jótékony hatással van a GC-s program sebességére.
    Mutasd a teljes hozzászólást!
  • A manuális memória kezelés meg nagyon munkaigényes és csak kényes feladatoknál jövedelmező az előnye.

    És ha nagyon akarod, még ezt is meg tudod tenni Java-ban (bár nem tudom Java 9 után mi lesz az Unsafe helyett off heap memória allokálására).
    Mutasd a teljes hozzászólást!