Java - text file memóriába?
Mondjátok, java-ban tényleg nincs olyan, mint pythonban ez:
Szóval, hogy egy NL karakterekkel sorokra bontott text file-t egyetlen művelettel töltsek be egy dinamikus méretű String típusú tömbbe?
Akármerre nézelődtem, csak olyat találtam, hogy olvassam be rekordonként (soronként) az egészet, ami viszont jóval lassabban megy, mintha egyetlen művelettel intézném.
(csak teszteléshez kellene, semmi éles dolog ;) )
■ with open("file.txt","r") as infile:
tomb=f.readlines()
tomb=f.readlines()
Szóval, hogy egy NL karakterekkel sorokra bontott text file-t egyetlen művelettel töltsek be egy dinamikus méretű String típusú tömbbe?
Akármerre nézelődtem, csak olyat találtam, hogy olvassam be rekordonként (soronként) az egészet, ami viszont jóval lassabban megy, mintha egyetlen művelettel intézném.
(csak teszteléshez kellene, semmi éles dolog ;) )
Régen nem írtam Java kódot,
Ezt lemérted? Honnan tudod, hogy jóval lassabb? Lehet, hogy csak Python-ban lassabb?
De persze van másik megoldás is, ugyan ehhez már Java 7 kell (és simán lehet, hogy lassabb, mint az előbbi):
Konkrétan pythonban néztem,
for rec in f:
pass # itt csinálok vele valamit
mintha egyesével olvasom:
rec=f.readline()
while rec != "":
pass # csinálok valamit
rec=f.readline()
Az első valami bufferelt olvasást végezhet, mert a sebessége nagyjából azonos a nyitó hozzászólásban vázolt két soréval, ha utána megyek végig a memóriában tárolt sorokon.
Az egész meg onnan indult, hogy lassúnak találtam a regexp feldolgozást pythonban, gondoltam, más nyelven talán gyorsabb. Erre kiderült, hogy nagyjából azonos programok közül a perl és a python a leggyorsabb, ha a sorokat regexp segítségével szűröm (50-100%-os eltérések vannak a perl-python javára).
Azóta persze játszottam olyat, hogy fájlból olvasás helyett két stringet adtam oda egymilliószor (vagy tíz-? már nem is tudom) a szűrőnek, így közel egyforma eredményt produkált a java is és a python is.
Viszont itt gyanús, hogy inkább a regex feldolgozó cache-el és ezért egyformák. Ezért kellett volna, hogy betöltsem memóriába az egész fájlt, ahogyan pythonban és azt ugyanúgy dolgozzam fel.
Ugyanis egyelőre van némi bizonytalanság a dologban: lehet, hogy mégis az I/O műveletek kavarnak be a mérésbe. Sajnos a BufferedReader-nek hiába adtam akkora buffer méretet, ami nagyobb, mint az egész fájl, ez nem segített.
Ha a hozzászólásokat ignorálod, csak a topiknyitót nézed, akkor itt a rövid történet: http://hup.hu/node/134550
(hozzászólásokat azért ne, mert teli van flame-mel ;) )
upd: az utolsó sort kerestem azt hiszem. Köszi!
VM indulás
Java esetén a Stringek kezelése jelentősen eltér más nyelvektől, ugyanis ha jól emlékszem minden karaktert mindenképpen több byte-on tárol, lehet hogy akár 3-4-5 byte is szükséges egyetlen karakterhez. Ennél sokkal jobb eredménnyel jár az ember, ha csak byteokat olvas be és kezel, mert akkor nincsen a hatalmas memóriaigény (mondjuk ekkor értelemszerűen elesik az ember a reguláris kifejezésektől).
Egyébként miért nem jó neked a soronkénti beolvasás? Akkor kisebb a memóriaigény, és soronként tudod az adatot feldolgozni, ahogy jön befele az adat, nem kell megvárni, hogy az egészet beolvassa a kód memóriába.
Ezzel tisztában vagyok (már
Arról nem beszélve, hogy C programnál végképp nincs VM indulási idő. :)
Ugyanez igaz egyébként az I/O-ra is: ha csak olvasok, de nem csinálok semmit a sorokkal, a futásidő is minimális egy bizonyos fájlméretig és közel azonos a különböző nyelveken írt programok esetében.
Ha a teszthez használt ~700ezer sor helyett 7milliót dolgozok fel fájlból, akkor csökken az eltérés, bár még mindig a python (és a perl, de azt most hagyjuk) a leggyorsabb.
A soronkénti beolvasás végeredményben jó, csak akkor valóban külön kell szórakozni az idő mérésével és jó lett volna, ha két, többé-kevésbé egyformán működő, teljes programot tudok hasonlítgatni.
Végül az encoding miatt
Már csak azért is, mert kíváncsi voltam, mennyit lassít az I/O az egészen. Hát nem sokat...
A python még így is gyorsabb regex feldolgozásban.
Nem akarom elhinni. :(
Java -version; uname -a mit
Java -version; uname -a
mit mond? Nem túl valószínű, de nem lehet, hogy kliens módban fut a jvm?Nálam így néz ki egyébként a sorrend. Kicsit egyszerűsítettem a patternen hogy legyenek matchelő sorok és kiírattam a ciklus tényleges futási idejét.
Openjdk 1.7, 64bit, próbáltam
Kernel verzióról nem tudok nyilatkozni, csak ha bekapcsolom őket.
A te változatod viszont közelebb áll ahhoz a sebességhez, amire számítottam. A mintán mit írtál át? Nem tartom kizártnak, hogy összefügg a változtatásod a sebességbeli eltéréssel.
Update: ami itt van az ölemben, 64bites xubuntu 14.04, a java ezt írja ki:
java version "1.7.0_55"
OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-1ubuntu1)
OpenJDK 64-Bit Server VM (build 24.51-b03, mixed mode)
A mintát erre írtam át,
^(\w+\s\d\d)\s(\d\d:\d\d:\d\d)\s(\w+)\s(\w+):\s(\[\w+\.\w+\])\s\w+:\s+(ACCEPT|REJECT|DROP)\s+(.*$)
Az alábbi az eredmény.
java -XX:+PrintGCDetails L1
Ha esetleg tudod anonimizálni a logod, akkor szívesen lefuttatom arra is. Csak érdeklődésből csinálod amúgy, vagy nagyon számít, hogy lehessen még húzni a teljesítményén? Profile-ozni lehet, ha nagyon kíváncsi vagy.
A teljes filet memóriába rántó, majd utána soronként végigtekerő megoldás pedig nem lehet gyorsabb annál, mint amikor azonnal feldolgozod. Ha már egyszer a kezedben van valami adat, az a legoptimálisabb ha fel is dolgozod. Különben még egyszer elő kell rángatni a memóriából. Ami a disk elérésnél összehasonlíthatatlanul gyorsabb, de nincs ingyen. Nagy mennyiségnél ez már okozhat észrevehető különbséget. Emellett ez a verzió nálam full GC-ket is nyomott, míg az eredeti L1 verzió csak minor GC-ket. Ha adtam neki 2G memóriát, akkor nem volt GC és kb 40 ms-sel volt gyorsabb az L1.
Részemről merő kíváncsiság,
Maga a log 160MB, a java-t volt, hogy 1G heap mérettel indítottam.
Kipróbáltam openjdk-t, oracle-t (mindkettő 1.7), semmi különbség.
Nálam egy i5-2520M procin, virtualboxban fut elsődlegesen, de kipróbáltam fizikai vason is, egy core2duo processzoros, 6-7 éves gépen is, ott asszem, a java is régebbi volt, de ugyanígy 64bites, a python változat ott is gyorsabb volt.
A minta kapcsán csak annyi érdekelt, hogy a zárójeleket nem vetted-e ki, mert azok, illetve a végén a .* esetleg sokat lassíthatnak rajta. (már nem tudom, hol olvastam erről. :( )
Az nem tudom, feltűnt-e, hogy amikor a teljes fájlt memóriába töltöm és ott megyek végig rajta, akkor külön mérem a beolvasásra fordított időt és külön azt, amikor a listán megy végig és ellenőrzi a minta illeszkedését.
De mondom, ez az egész nem arról szól, hogy gyorsítani akarok valamin, pusztán nem akarom elhinni, hogy egy java program, nagyjából azonos működés esetében lassabban fut le, mint egy python.
Python amúgy milyen verzió?
Azt kipróbálhatod még amúgy, hogy nem új matchert hozol létre minden iterációra, hanem Matcher.reset()-et hívsz. Valamennyit ez is gyorsít.
2.7 szintén.A reset csak
A reset csak minimálisan gyorsít, nem beszélve arról, hogy a pythonban sem trükközök semmivel.
Viszont... ha a match() helyett find()-t használok és a minta végéről lehagyom a "(.*$)" karaktereket, akkor már gyorsabb a pythonnál (úgy is, hogy a pythonban is kiszedem ezeket a karaktereket a mintából)
Ami legalábbis érdekes.
Megtennéd, hogy a minta
Nálam a java-t erősen felgyorsította ez az apróság, a pythont kevésbé.
Durván 30%. $ time java L1;
675 ms
162409,213960
real 0m0.759s
user 0m0.916s
sys 0m0.076s
162409
real 0m1.112s
user 0m1.089s
sys 0m0.023s
376369
Elapsed time: 537ms
Known:0 unknown:376369
Elapsed time: 500ms
real 0m1.132s
user 0m1.596s
sys 0m0.223s
162409
213960
real 0m2.447s
user 0m2.376s
sys 0m0.071s
Ha a sebesség számít, a
Remélem, ezt csak viccnek
Úgy gondolod, hogy fut a program és beolvasott soronként indít egy linuxos utility-t? Vagy hogy?
Nem, abból indultam ki, hogy
Nekem viszont van egy készülő
A mintaillesztésből
Van jelentősége? Jelenleg
Jelenleg pusztán az érdekel, hogy miért működik ennyivel lassabban a java regexp motorja a pythonénál.
(Egyébként gondolod, hogy csak l'art pour l'art akarom szétszedni a beolvasott sorokat? ;) )
Van
Na jó, akkor ezt most
Mit nem értek?
Van egy program, amit hosszú
Észrevettem, hogy lassú a regexp feldolgozás benne, több nagyságrendet gyorsít, ha például a beolvasott szöveg mezőit nem regular expression segítségével szedem szét, hanem ú.n. whitespace karakterek mentén egy split függvénnyel darabolom (amit adott esetben nem tehetek meg sajnos, valójában egy tisztességes parsert kellene írnom, de egyelőre elégséges a regexp használata, kihasználva a python spec. lehetőségeit, pl. a groupokat).
Ennyi volt az előzmény.
Pár napja eszembe jutott mindez és előszedtem a maradék java, ruby, C ismereteimet meg millió tutorialt és megnéztem, hogy más nyelveken mennyivel gyorsabb. És itt ért a meglepetés, mert mind C-ben, mind java-ban jóval lassúbb lett a teszt, (szerintem) azonos mintákkal és azonos input fájlokkal. Természetesen nem egy-egy futtatásról van szó, hanem rengetegről (most már közel járhatok a százas értékhez/nyelv)
A tesztben pont az lenne a lényeg, hogy az adott nyelveken mit lehetne tenni, hogy gyorsabb legyen a minta illesztés, mint pythonban (különösen C és java esetében izgalmas, mert ezek ugye eleve gyorsabbak egyéb téren).
Lehet, hogy a Python valami
És ettől gyorsabb, mint a
Épp azon problémázok, hogy bár a pythont lassúnak találom helyenként, az egyébként jóval gyorsabb programnyelvekben megírva ugyanazt, sokkal lassabban működő kódot kapok.
Amit még el tudok képzelni,
Viszont most már feltehetnéd valahova a fájljaidat (a feldolgozandóval együtt), hogy megnézhessem, mert elkezdett érdekelni a dolog.
minta
python
A feldolgozáshoz használt állomány egy linuxos kernel log (openwrt syslog-ng kimenet) ilyen jellegű sorokkal:
Ilyenből, csak változó tartalommal (fontos a változó tartalom!!) van kb. 700ezer. A fenti persze a valóságban egy sor, de nem tudtam úgy beilleszteni.
Pont egy ilyen változó
C Bocs, de privát logot nem
Bocs, de privát logot nem szívesen adnék ki.
Nincs kéznél egy darab linuxod sem?
A /var/log/syslog v. messages fájlokban kb. ugyanez van, max. a mintám nem illeszkedik rá, mert az elején a dátum más formátumú.
Ok, köszönöm.