Java 6 tényleg ilyen gyors?

Fórumok

Még középiskolában, egyik informatika órán írtunk egy programot, ami prímszámokat keres. Akkor láttam, mennyire erőforrás-igényes erőből keresni a prímszámokat (egy számot úgy vizsgáltam meg, hogy elmentem ciklussal szám-1 -től egészen 2-ig és ha valami oszotta, akkor nem prím, nem foglalkozva sem azzal, hogy a szám/2 -nél nagyobbak eleve nem lehetnek osztói, sem azzal, hogy a páros számok pl eleve kilőve, ilyesmi) és azóta rajtam maradt, hogy lefuttatom azon a hardveren, amivel találkozom.

Nagyjából erről van szó:

int primes=0, prime, i, j;
for(i=2;i<=32768;i++){
prime=1;
for(j=(i-1);j>1;j--){
if(i%j==0) prime=0;
}
if(prime==1) primes++;
}

C-ben írtam meg persze régebben, de most, hogy az ember könyebben hozzáfér duplamagos processzorokhoz, áttértem Python-ra és több szálon egyszerre kerestem prímeket, minél gyorsabban találta meg őket, annál jobbnak véltem a processzort. Mindegy volt, hogy C++-ban, Python-ban vagy Java-ban írtam meg, nagyjából ugyanolyan eredmények lettek. Viszont most, hogy feltettem a Java 6 update 3-at és abban megcsináltam, azt tapasztaltam, hogy a program 4x, de sokszor 6x gyorsabban futott le.

Jelenleg a teszthardver egy Pentium 4 HT 3.0 2048K, illetve egy AMD Athlon 3500+

Ennyire gyors lenne az új JRE? Ennyire készültek a új processzorokra?
Van valakinek valami rálátása, esetleg valaki, aki nagyobb projecten dolgozik Java-ban?

Hozzászólások

A hatos java már tényleg egész gyors (megjegyzem, nekem az ötössel sem volt különösebb problémám).

Így egy matematikus nem keres prímszámot soha. :D Ha lehet kerüljük el az exponenciális" algoritmusokat!

Nagyon gyors. Kép átméretezést mértem most vele, és kb. 10%-kal lassabb csak egy C-s megvalósításnál. Fordításnál is látványosan gyorsult, illetve gyorsabban is indul el.

A JVM Jit fordítója az ilyen csak egyszerű aritmetikát tartlamazó kódot lényegében c-vel egyező minőségű kódra fordítja, úgy futtatja.
Egy haverom is mért ilyeneket, a BEA JVM már régebben (kb másfél éve) is ugyanolyan gyors volt, mint a C, akkor azt hiszem még a Sun JVM jelentősen lassabb volt.

egy ilyen példánál ez nem jellemző, de pl. j2ee alkalmazások általában jobban futnak.. de hangolás kérdése is. A JVM eléggé kihasználja az op. rendszer lehetőségeit, pl. NIO, threading, itt sokszor egy az egyben az számít, ami "alatta" van. Olyan is van, hogy valami jól meg van csinálva egy op. rendszerre, a másikra meg nem, de lehet hogy akár egy minor verzióval később már az is jó lesz. A Netbeans pl. Windowson fut a legjobban, groovy alkalmazások linux gyorsabban indulnak, de solarison egy picit gyorsabbak, volt egy mindössze pár class-ból álló, log fájlokat feldolgozó, multithread alkalmazás, ami adott hardveren windows alatt teljesített a legjobban, de ez sem jelent semmit, mert nagyon optimalizálva volt és ha nem windows alatt fejlesztjük, akkor lehet hogy egész másképp csinálunk benne néhány dolgot.

Aha ezt én is olvastam, hogy rengeteget gyorsult, de memóríát azt nagyon sokat eszik. Ha azt is lefaragják annyira hogy annyit egyen mint a c, és legalább olyan gyors legyen mint a c (most olyan 15-30%-al lassabb kb, van ahol kevesebbel van ahol többel.) akkor elkezdem tanulni azt is. Szépen fejlődik. (De mondjuk a C is szépen fejléődik).

Sosem fogják tudni C szintre csökkenteni a memóriahasználatot, mivel C az mindent a programozó kezébe ad, a Java pedig 'managed' - tehát megvédi a programozót amennyire tudja.

Viszont ha valaki tisztában van a memóriakezelésének trükkjeivel - pl. StringBuffer, vagy általában képes profilingolni, akkor egész jó eredményeket érhet el Java -val is.

Mondjuk ez igaz. Mentségemre legyen szólva életemben 2* írtam java kódot eből egy az hello word szintű volt a másik lottóhúzásszimulátor. (Ez utóbbinál döntöttem úgy hogy a C nekem jobb mert akkoriban vagy 20*gyorsabban futott a C, ha jól lártom ezt már letornázták szépen, olyan -30% körüli értékre, de soha nem próbáltam)

Egy kiránduláson is könnyeben mozogsz egy hátizsákkal, amiben van pár szendvics, egy kés meg egy hálózsák, de nem is tudod előhúzni belőle a házimozirendszert. Ára van mindennek :)

--
Nem győzhetsz. Nem érhetsz el döntetlent. Még csak ki sem szállhatsz a játékból.

Félre ne érts, én nem vagyok olyan elborult ember aki szerint csak ez vagy csak az a jó. Mindennek megvan a maga célja az egyik inkább erre jó a másik arra. Javaban többnyire sokkal gyorsabban megírható minden, mint C-ben, cserében C-ben gyorsabb, kevesebb erőforrásigénnyel. Mindíg a feladathoz kell megválsaztsni az eszközt, szerintem. Az én céljaimnak jobb a C, mert nekem gyors programok kellenek (többnyire matematikai problémák megoldása), tudom akkor jobb az ASM, de nincs kedvem külön megírni minden architektúrára/gépre ugyanazt a kódot.

Nem értettelek félre, nem is igazán Neked szólt, inkább azoknak, akik elvakultan hisznek egy útban, vagy pont azoknak, akik egy eszközt - mélyreható ismerete nélkül - nagy mellénnyel szidnak pár frázisból összekovácsolt mondanivalóval. Ami a Java-nal elég gyakori, sokan le ,,lomha sz*rozzák'' közben azt sem tudják, milyen könyvtárak vannak hozzá vagy akár 10%-ot sem ismernek a felhasználási területükből esetleg üzleti hátteréről.

--
Nem győzhetsz. Nem érhetsz el döntetlent. Még csak ki sem szállhatsz a játékból.

Ezzel kapcsolatban negy dolgot jegyeznek meg:

1. Van Real Time Java specifikacio is, ami lenyegeben a programozo kezebe adja a sajat objektumai altal foglalt memoriateruletek kezeleset (pontosabban memoriateruletek kezeleset amikbe az objektumait allokalhatja).
2. A JRockit-ban mar van soft-realtime, magyarul te mondod meg maximum mennyi ideig allithatja le a szalakat szemetgyujteshez.
3. Egy byte[] az 1 objektum. De eleg nagy byte[]-ot is allokalhatsz. Ezen belul meg ugy garazdalkodsz ahogy akarsz, effektive te menedzseled a memoriat :-) Persze fragmentalodast is neked kell lekezelni.
4. Lehet direct ByteBuffer-t allokalni ami nem a heap-en lesz lefoglalva (csak a ByteBuffer objektum), es nagyjabol hasonlo dolgokat csinalhatsz vele mint egy byte[]-el.

Udv,

Finrod

Kiváncsi lennék pl. java Vector vs. c++ stl vector , integerek kezelésében, hogy szuperalnak.
Ugyan ara kereses vagy rendezes algoritmusra.

Ha elfelejtem, hogy C++ hasznalkok (igyekeztm javas jelenteset bele kodolni) akkor kb. 2x gyorsabb stl:vector , ha eszembe jut akkor 3x (tovább már nem gondolkoztam ). kb. 2* annyi memet is eszik java -s java.util.Vector -osdi.
{vectorba integer parok pakolsa majd, maximum kereses, megsemmisites volt a jatek,} ezt ciklusba.

Nem közvetlen neked mondom, csak ide csatlakozik a téma. Szerintem ezek a gcc vs java, egyik_distro vs másk_distro. A világ legmeddőbb vitái. Tök fölöslegesek, ugyanis ezek inkább "vallási", mint objektív viták lesznek. Szerintem olyan, mint ha az almát és a körtét hasonlítanánk össze, hogy melyik "gyümölcsebb".

ArrayList kapott a konstruktorában paramétert?

Szinkronizált - Javaban egy nagyon alapvető fogalom - más szavakkal annyi, hogy reentránsak-e az adott osztály metódusai. Értelemszerűen ha igen, akkor az extra (ezt biztosító szinkronizációs) kód (monitor lock, unlock) miatt egy szálon lassabban fog futni az adott program. Ha nem, akkor meg több szálnál jöhetnek majd érdekes hibajelenségek...

Nem, csak template marhasagot, a C++ sem kapott.

Aha, Tehat Java Vector , garantalja, hogy egy konteneren 8 magos rendszerben 16 szalon, ha hivogatom az add -ot akkor nem omliko ossze , az ArrayList meg nem tudja ezt, zagyvasag vagy kivetel keletkezik.. ugye ?

Netan minden methodus elejen locol, a vegen unlocokolja az osszes methodust a tarolomak, vagy ettol rafkosabbat jelent?

Próbáld ki úgy is, hogy az ArrayList-et new ArrayList(n) -nel hozod létre, ahol n egy int és nagyobb, mint a listába pakolandó összes várható elem száma.

Hogy a javadoc-ból idézzek:

"Each ArrayList instance has a capacity. The capacity is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically. The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost."

Azt hiszem 10 az alap méret, ha nem adsz meg semmit, és amikor ez elfogy, akkor foglal egy új, 1,5x akkora array-t. És amikor az elfogy, akkor megint 1,5x annyit. Tehát mondjuk 10000 elem bepakolása közben jópárszor másolgat ide-oda array-ok között a jvm... Te meg nem biztos, hogy ezt akartad lemérni.

De a meres fontos resze volt.
A c++ vector, ha jol tudom ugyan ezt üzi 2x mokaval, ha nem adok meg neki semmit.

Most megneztem, ha ugyan azt az elemet addolom 6* gyorsabb volt C++ . (6 sec, vs 30 sec (ArrayList)), (new es delete idok kikuszobbolesere, latszolag az kb. egyezett.)

Nezek egyet reserve -elesel is, a bekesseg kedveert. :)

Milyen jvm opciókkal mérsz (remélem -server legalább)? Mi méri az időt: a program, vagy a shell? Látsz garbage collection-t a mérés közben?

Sok az a 6x különbség, csak azért kérdem. Adsz időt a Hotspot-nak a bemelegedésre? Úgy értem ugye az első párszáz iteráció eredményét eldobod?

Lefoglalva 12 sec vs 3 sec 4* szorzo.

Nem hasznalok servert, g++ sem hasznalok march -ot, meg profilingot meg etc -t :)

Az egesz kod a kodon belul 128 szor van iteralva, minden iteracio vegen szoveg kiiras van, szemre egyenletes.

Amikor tobbet alokaltam, valoszinuleg a gc dolgozhatott, de c++ is volt explicite delete :).

Shell meri az idot.

Tudom a modszerem miatt kap java kb. ~1 sec -el tobb idot .

szerk:
Select the Java HotSpot Server VM. On a 64-bit capable jdk only the Java Hotspot Server VM is supported so the -server
option is implicit.

64 capable a cucc.

Az ok, de ha explicite -64-et nem irod ki parameterkent, akkor altalaban 32 bites lesz a jvm (ami abszolute nem baj).

A -server és a -client között az egyik fő különbség az, hogy milyen hamar áll át a vm natív végrehajtásra egy adott metódus esetén. A 128 azt hiszem még elég kevés a -client-nek, én mindenképp futtatnék egyet -server-rel.

Nalam ki van valasztva, hogy java 64 bites javat hasznaljon. (eselect, java-config)
-server -d64 ugyan az.


$ eselect java-vm list
Available Java Virtual Machines:
  [1]   emul-linux-x86-java-1.6 
  [2]   sun-jdk-1.5 
  [3]   sun-jdk-1.6  system-vm
$ java -version
java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) 64-Bit Server VM (build 1.6.0_03-b05, mixed mode)

Szerintem a legbelso ciklus elso lefutasakor, mert kod mar JIT-elve van.
Nem latom ertelmet feloraig tesztelni.


$ time ./vector4 >/dev/null

real    0m1.896s
user    0m1.808s
sys     0m0.023s
$ time java -server -d64 main >/dev/null

real    0m10.454s
user    0m9.786s
sys     0m0.314s

kiiras adott hozza ~2 sec -et a realhez, pedig nem sokat irtam.

A 64-bites java sok objektum eseten kevesbe hatekony lehet, mivel az objektumok overheadje itt nagyobb.

Ha csak sok primitiv long-od meg double-od van, akkor erdemes 64 bites JVM-et hasznalni.

Meg persze ha nagyon sok adatot kell menedzselj, de akkor jobb ha felkeszulsz arra, hogy 1 giga feletti heap-ek eseten a GC nagyon sokaig meg tudja fogni a szalakat. Celszeru ilyenkor tuningolni a GC-t, vagy ha van ra lehetoseged, JRockit-et hasznalni, ahol megmondhatod hogy meddig foghatja meg a szalakat a GC.

Udv,

Finrod

Ha reprezentativ eredmenyt akarsz akkor a kovetkezo dolgokat tedd meg:

1. Explicite add meg a -server opciot.
2. Egy csomo iteraciot tekerj (negyedora-felora is akar,, es az elejen dobd el az eredmenyeket). Egy ido utan (ez akar 5-10 perc is lehet) a JIT compiler ujraforditja kodot az addigi informaciok alapjan optimalizalva). Teged csak az ezutani eredmenyek erdekelnek.
3. NE IRJ KI SEMMIT meres kozben.

4. Java-ban ne hasznald a System.currentTimeMillis()-t idomeresre. Pontatlan. Nagyon (kb. 20-50msec a granularitasa, es 1 masodpercnyi hibat is osszeszedhet, vagy akar ennyivel kisebb(!!!) eredmenyt is visszaadhat azonos szalon elozo hivashoz kepest).
Hasznald a System.nanoTime()-ot, viszont csak nagyobb adag meresre, mivel a System.nanoTime() is koltseges lehet bizonyos platformokon (1ms is lehet, ami rovid szamitasok idejenek meresenel, vagy HPC rendszer latency benchmark-olasanal azert jelentos). Ha egy egy masodperces muveletet mersz currentTimeMillis()-el akkor akar +/-(!!!) 100%(!!!) hibat is osszeszedhetsz, mig nanoTime()-al kb. 0.1% a meresi hiba.

Udv,

Finrod

A new operator a JVM legoptimalizaltabb kodja, ertheto okok miatt. Gyakorlatilag legalabb olyan gyors mint a C++ new operatora, azonos architektura (32 vagy 64 bit, azonos processzoron) alatt.

Viszont ertheto okokbol, egy 32 bites C++ meg egy 64 bites Java kod osszehasonlitasa nem egeszen fair (nagyobb a 64 bites Java objektum referencia mint a 32 bites Java objektm referencia, gyakrabban kell malloc-ot hivnia a JVM-nek).

Udv,

Finrod

A Vector igen, gyakorlatilag az összes metódusa synchronized kulcsszóval van ellátva. Az ArrayList-nek meg nem.

De nem a tartalomnak, hanem magára a Vector objektum monitorára lock-olnak ezek a metódusok (ajánlott irodalom: http://java.sun.com/docs/books/jls/second_edition/html/memory.doc.html)

Igen, ArrayList-nél előfordulhat, hogy az egyik szál add-ot hív, miközben egy másik szál éppen iterálgat végig a Vectoron... De ez akár egy 1 magos rendszerben is előfordulhat, ha éppen az iterátor szálat suspendeli a CPU ütemező, és a feléledő szálon pedig ráfut az add()-ra.

Ez egy nagyon jó könyv a témáról, ha érdekel: http://jcip.net/

Az egyik szal add-ol, a masik szal iteratorral iteral problemat Vector-nal is megszivod, nem csak ArrayList-nel.

A kulonbseg az, hogy a modosito muveletek konkurrens hivasa Vector-nal biztonsagos, ArrayList-nel meg nem biztonsagos (ha a belso array-t mindket szal novelni akarja, akkor az egyik altal hozzaadott elem elveszhet).

Egyebkent valaki emlegette a StringBuffer-t mint teljesitmeny novelo tenyezot (string konkatenalashoz kepest). Ez szinten szinkronizalt metodusokkal rendelkezik, a szinkronizalatlan verzio a StringBuilder.

Ez így van.
A Vector egyébként elég archaikus dolog szvsz, amit az egységes Collection Framework bevezetésekor (Java2) kompatibilitási okokból meghagytak.
Szinkronizált listát bármilyen normál listából lehet készíteni (ArrayListből is). Egyszerűen "be kell csomagolni", valahogy így:
List syncedList = Collections.synchronizedList(aList);

Amit leirtal, annak semmi koze a reentrans-saghoz.

A reentrans-sag akkor erdekes, hogy ha azonos szalrol akarod azonos objektumon meghivni ugyanazon vagy mas metodust meg egyszer, mielott az elozo hivasbol kileptel volna (pl. rekurziv fuggvenynel).

A szinkronizalt annyit tesz Java-ban, hogy tobb azonos objektumon szinkronizalo synchronized blokk kozul egyszerre garantaltan csak egy fog futni, valamint (Java 5-tol felfele) a kesobb indulo blokk garantaltan lat minden a korabban befejezodo blokk thread-je altal a szinkronizalt blokk-bol kilepesig vegzett muveletet (a szinkronizalt blokk elotti muveleteket is).

Java 6-ban jellemzoen a tobb egymasba agyazott de azonos objektumon szinkronizalo blokk mar nem sok overheadet jelent a csak a legkulso blokk-on levo szinkronizaciohoz kepest (mar nalad levo monitorra szinkronizalas koltsege minimalis).

Csianltam egy gyogy tesztet thread kezeles -re.
java viselkedeset ultetem at (tehat nincs kihasznalva, hogy mast is hasznalhatbnek c++ -ban) C++ + pthread -re (java is ezt hivja).

Ket szalnak kellett felvaltva csokentenie , egy erteket (2^24), szabaly az volt, aki csokentette az legkozelebb at adja egy masik szalnak a lehetoseget. (wait, notyfi -vel, ment a moka)
(1 mag)
JAVA:
real 0m50.130s
user 0m13.201s
sys 0m27.545s
C++:
real 0m37.756s
user 0m5.426s
sys 0m25.809s

Kivancsi voltam, hogy mibe kerul a kenyelmes szalkezeles.

Annyit azért hozzátennék, hogy ez egy elég elavult módja a szálkezelésnek.
Ha sokkal kevésbé elrontható és emellett lényegesen jobban skálázódó szálkezelést szeretnél, érdemesebb szálvédett fifo-kon keresztül kommunikáló szálakat tervezni, amik a fifo-kból jövő "job"-okat hajtják végre.

C++-ban ennek tetejére célszerű létrehozni egy kis framework-öt, ami functor-template-ekkel elfedi ezt sima metódushívásokká.

Én is pont ezt művelem eszembe jut valami bevágom módosítom, csak nekem írás közben.... Ezt egy ritka gondolkodásmód miatt van (nagyvalséggel). Beszéltem (annó az 1800-as években) egyszer erről egy pszichológussal. Még középsuliban volt, mert mixeltem a történeti szálakat a dolgozataimban. Lehet neked is ez van. Szóval ez van. :D

Nem, papiron nem tudok beszurni :) , ezert kenytelek vagyok, sokat kihuzni vagy konzisztesen folytatni.

De egyesek szerint hyperaktivitas tuntei mutatkoznak rajtam :)
Pl. helyesiras szabalyait ismerem, de nem alakalmzom oket, ez allitolag tunet.
Regebben, ha iszogattam, meg not a mozgas igenyem, siman masztam fara, futva jottem haza, mert minek setalni az LASSU.

Altalanos suliban picologus nezte ertelmi kepessegeimet :)
Es ami erdekes volt, hogy a nehez feledatokat szinte soha nem basztam el, a konyueket meg igen. Talan ezert, mert nem kotottek le figyelmemet az egyszeru kerdesek, ezert leszartam.

Valami ilyesmit mondott a picologus 7 -es koromba.
Vizualis felfogas: atlagostol magasabb (jo)
Verbails felfogas: altagostol alacsonyabb
IQ: jo (1 evel idosebbekhez nezve, a tobbi ertek is 1-5 mondott eredmenyt mindhez)
Figyelem koncentracio: atlagos
Matematikai logika: magas

Na tömören amit nekem detektáltak az az ugynevezett erősen domináló deduktív divergens gondolkodás + hiperaktívitás. Nekem a legtipikusabb tünet, hogy nem tudok egyedi feladatot jól meegoldani (pl: sudokut megoldani, mert általános elméletet akarok csinálni mindenre, ez pl a sakkra is igaz, emiatt pocsékul sakkozok, mert nem tudok konvergensen jól gondolkodni, egy adott feladatra koncentrálva). Sudokunál nem is nézem a számokat amik benne vannak a táblázatbaan, hanem általános megoldáson agyalok, kitalálok egy elméletet azt használom, és ha elakadok akkor megint tovább agyalok rajta, ami mindíg és mindenhol jó. Pl most írok egy olyan algoritmust C-ben ami tippek nélkül megoldja a sudokutáblát. Egész jól haladok vele. A nem tul bonnyolultakat már determinisztikusan megoldja tipp nélkül, A bonyolultabbaknál van benne egy kis back-track is. De soha nem kell 20-30 "tippnél" többet végrehajtania. Ha kész van a megoldás elküldöm a C (vagy C++)-kódot, nem tudom miben írtam most hirtelen, vagy 2-hónapja nem nyultam hozzá sok a dolgom.

Mégis mennyi volt a 4x-6x gyorsabb futás? (Próbaképp linux alatt lemértem 1.6.0_10 java-val, és kb. 4-5 sec alatt "fordul", ha sok időm lesz, megnézem Solaris alatt is, de szerintem nincs nagy különbség. Nagy különbség algoritmusváltáskor lehetne :) (Erathosztenész, Atkin és társai)).

A jelen esetben nem annyira szembeötlő, mert a címből is látszik a régi probléma. De ha a szokásos, "firefox bookmark probléma" jellegű cím van, és örülsz, hogy lám, másnak is van gondja vele, nézzük, hátha tud valamit, aztán kiderült, hogy egy azóta megszűnt verzióban már két éve kijavított hibáról van szó... az lelombozó.

Pláne fasza, ha a hozzászóló olyat kérdez 3-4 év távolából, hogy "mi van a logban?". És minősített esete ennek a "nullázás", amit szívem szerint örökös bannal díjaznék, azaz amikor valaki csak azért szól hozzá egy témához, hogy Ő legyen az első - épp ezért a hozzászólás érdemi része 0%.

32767 elemől álló ciklus? Ennyi idő alatt a JVM be sem melegszik :-p