[megoldva] Kimenet szortírozása (grep általánosítás)

Van egy PHP programom, ami viszonylag erőforrásigényes szövegfeldolgozást végez (10-12 órán át fut).
Bár három (szöveges) eredményfájlra van szükség, úgy döntöttem, hogy egyetlen kimenetet gyártok, amelyben soronként jelzem az első karakterrel, hogy melyik kimenetbe szánom az adott sort. Utána három greppel szortírozom a sorokat: grep "^a" ...; grep "^b"...

Ez utóbbi hármas grep valahogy egész biztosan feljavítható, ugyanis háromszor kell átnyálazni a fájlt, háromszor kell ránézni minden sorra, pedig elvileg elég lenne csak egyszer. Ha van ötletetek szép, időtakarékos megoldásra (pl. egy C/C++ program formájában), örömmel veszem. Akár még a PHP program szintjén is elfogadok tanácsot (pl. nyugodtan irányítsam háromfelé, nem lesz attól sokkal hosszabb a futás...)

Úgy is fogalmazhatnék, hogy egy általánosabb grep-re lenne szükségem, ami nem csak "igen-nem" jelleggel nézi valaminek a meglétét (és aszerint írja ki vagy sem), hanem lehetővé tenne többfelé terelést is az első karakter alapján.

Hozzászólások

PHP script 12 órán át fut? Jó ég. Mindenképpen kell rá írni valami alternatívát egy natív nyelven és cron-ból futtatni, a PHP csak az eredményét dolgozza fel. miért nincs három külön fájl, ha amúgy is három kiemenetre szánod?

--
http://sandor.czettner.hu
http://turaindex.hu

ennyiből nekem nem világos, hogy miért jobb markert tenni a kimenetbe, és az alapján utólag szétválogatni, miért nem jobb egyből több külön fájlba kiírni...

Mekkora a szövegfájl, amit szét kell válogatni?
Mennyi a grep futási ideje a mostani módszereddel? Hány százalékos javulást vársz a mostani PHP+grep együttes futási idejéhez képest? Mennyi időt tudsz feláldozni az idődből ennek a gyorsulásnak az eléréséhez? Esetleg a PHP leváltásával sokkal nagyobb gyorsulás érhető el?

Amiatt láttam jobbnak markereket használni, mert több szegmenst dolgozok fel, és a wget-eket indító shell szkriptben szeretem megadni a kimenet nevét; így gyorsabbnak és egyszerűbbnek tűnt a megoldás. Meg az örökös (többfelé történő) fájlnyitogatás is idő. De lehet, hogy ezt tényleg átgondolom, és inkább GET paraméterként adom meg a kimeneti fájl(ok) nevét.

A teljes (PHP+grep) futásidőnek kb. 20%-át teszi ki a grep; ennek a harmadolódásában bízom.
A PHP leváltásával persze lehetne gyorsítani, de annyira bonyolult (sok hónapon keresztül, sok ember ötleteinek bevonásával készült), hogy nem merném bevállalni az átírását. A fő PHP program kb. 600 soros, de vannak egyéb PHP alkatrészek is.

Eloszor is ne wgettel csinald, hanem php-vel egyenesen, masreszt pedig ha egyszerre nyitsz 3 file handlert, akkor mindharom file meg van nyitva, es egymas utan irkal bele a szal, nem kell mindig ujranyitogatnia. Szerintem igy tudod a leggyorsabban megoldani (marmint ha PHP-nel maradunk persze).

Nem is értem. Ha már feldolgozod az adott sort, miért nem teszed be a megfelelő (kimeneti) file-ba?

Ha mindenképpen "válogatni" akarsz, akkor egy awk-os megoldás, bár nem biztos, hogy gyorsabb:


#!/usr/bin/gawk -f

BEGIN { FS = "" }

($1 == "a") { print $0 >> "a.txt" }
($1 == "b") { print $0 >> "b.txt" }
($1 == "c") { print $0 >> "c.txt" 

Természetesen, előtte törölni kell a fájlokat: a.txt, ...

De, ilyen feladatokra jó eséllyel az awk a leggyorsabbak között van, mert nincs "megterhelve" sok egyéb funkcióval.

Amit a fentin gyorsítani lehet, az a mezőkre tördelés kiküszöbölése, amit gnu awk-kal így lehet elérni:


awk -vFIELDWIDTHS='1 1' '{ print > $1 ".txt"}' log

Tkp. így is tördel mezőt - összesen kettőt, az első, és (azt már feleslegesen) a 2. karaktert teszi $1-be és $2-be.

Az üres FS (= minden karakter 1-1 mező) felálláshoz képest már a 4M-s példafájlomon is láttat különbséget a time:


$ time awk -vFIELDWIDTHS='1 1' '{print > $1 ".txt"}' log

real 0m0.094s
user 0m0.080s
sys 0m0.016s
$ time awk -FS='' '{ print > $1 ".txt"}' log

real 0m0.134s
user 0m0.100s
sys 0m0.028s

Pardon, az üres FS-nek parancssorban a -F='' felel meg, de úgy még lassabb:

$ time awk -F='' '{ print > $1 ".txt"}' log

real 0m0.320s
user 0m0.284s
sys 0m0.032s

Nnnna, ezt is túlragoztam.

A te két változatod gyorsabb mint az enyém. Egy kb. 263 MB-os fájlon kipróbáltam, és az enyém kb. 50%-kal volt lassabb. Viszont a te két változatod kevesebb mint 1 másodpercben tért el, így a nagy nyereség -- szerintem -- a feltételvizsgálat helyetti 'print > $1 ".txt"' megoldásnak köszönhető. Ez rendben is van. A 263 MB-os fájlon az eredmény nálam:


$ time awk -vFIELDWIDTHS='1 1' '{print > $1 ".txt"}' log

real	0m31.959s
user	0m26.260s
sys	0m1.590

Ami meglepő lett, az az, hogy ugyanezen a fájlon a 3 grep sokkal gyorsabb:


$ time grep '^a' log > a.txt; time grep '^b' log > b.txt; time grep '^c' log > c.txt

real	0m7.704s
user	0m4.400s
sys	0m0.920s

real	0m9.833s
user	0m1.910s
sys	0m0.720s

real	0m8.566s
user	0m2.090s
sys	0m0.880s

Rövid töprengés után arra jutottam, hogy az lehet az oka, hogy a szabad memória nagyobb mint a fájl, így cache-ből olvassa a grep (is), ezért lett gyorsabb. Mivel a mérés előtt is futtattam már a te megoldásodat is, ezért az is a cache-ből olvasott. A mérést fordított sorrendben elvégezve, gyakorlatilag ugyanezt az eredmény kaptam.

Mivel az indításkor a szabad memória kb. 500 MB volt, így készítettem egy 2,63 GB-os tesztfájlt, és megismételtem a tesztet:


$ time grep '^a' log > a.txt; time grep '^b' log > b.txt; time grep '^c' log > c.txt

real	1m41.283s
user	0m46.990s
sys	0m7.650s

real	1m16.525s
user	0m21.610s
sys	0m9.190s

real	1m25.122s
user	0m21.030s
sys	0m8.410s

$ time awk -vFIELDWIDTHS='1 1' '{print > $1 ".txt"}' log

real	5m8.724s
user	4m22.750s
sys	0m15.950s

$ time awk -F '' '{print > $1 ".txt"}' log

real	5m3.136s
user	4m19.250s
sys	0m14.440s

És a grep(ek) még mindig gyorsabb(ak). A két változatod között még mindig elhanyagolható a különbség.