source - unix shell awk




Qual è la differenza di prestazioni tra gawk e...? (4)

Ecco un equivalente in PHP:

$ time php -R 'if(preg_match("/hit \d*0 /", $argn)){$f=preg_split("/\s+/", $argn); echo $f[3]." ".$f[0]." ".$f[2];}' < file > /dev/null
real    2m42.407s
user    2m41.934s
sys 0m0.355s

rispetto al tuo awk :

$ time awk -f tst.awk file > /dev/null
real    0m3.271s
user    0m3.165s
sys 0m0.104s

Ho provato un approccio diverso in PHP dove eseguo l'iterazione manuale del file, questo rende le cose molto più veloci ma non sono ancora impressionato:

tst.php

<?php

$fd=fopen('file', 'r');
while($line = fgets($fd)){
    if(preg_match("/hit \d*0 /", $line)){
        $f=preg_split("/\s+/", $line);
        echo $f[3]." ".$f[0]." ".$f[2]."\n";
    }
}
fclose($fd);

risultati:

$ time php  tst.php > /dev/null 
real    0m27.354s
user    0m27.042s
sys 0m0.296s

Questa domanda è stata discussa qui su Meta e la mia risposta fornisce collegamenti a un sistema di test per rispondere a questa domanda.

La domanda sorge spesso sull'opportunità di utilizzare gawk o mawk o C o qualche altro linguaggio a causa delle prestazioni, quindi creiamo una domanda / risposta canonica per un programma awk banale e tipico.

Il risultato di ciò sarà una risposta che fornisce un confronto tra le prestazioni di diversi strumenti che eseguono le attività di base per l'elaborazione del testo di regexp matching e field splitting su un semplice file di input. Se lo strumento X è due volte più veloce di ogni altro strumento per questa attività, allora queste sono informazioni utili. Se tutti gli strumenti impiegano circa lo stesso tempo, allora anche queste sono informazioni utili.

Il modo in cui questo funzionerà è che nei prossimi giorni molte persone contribuiranno con "risposte" che sono i programmi da testare e quindi una persona (volontari?) Li testerà tutti su una piattaforma (o alcune persone testeranno alcuni sottoinsiemi sulla loro piattaforma in modo che possiamo confrontare) e quindi tutti i risultati saranno raccolti in un'unica risposta.

Dato un file di input di 10 milioni di linee creato da questo script:

$ awk 'BEGIN{for (i=1;i<=10000000;i++) print (i%5?"miss":"hit"),i,"  third\t \tfourth"}' > file

$ wc -l file
10000000 file

$ head -10 file
miss 1   third          fourth
miss 2   third          fourth
miss 3   third          fourth
miss 4   third          fourth
hit 5   third           fourth
miss 6   third          fourth
miss 7   third          fourth
miss 8   third          fourth
miss 9   third          fourth
hit 10   third          fourth

e dato questo script awk che stampa il 4 ° quindi il 1 ° e il 3 ° campo di ogni riga che inizia con "hit" seguito da un numero pari:

$ cat tst.awk
/hit [[:digit:]]*0 / { print $4, $1, $3 }

Ecco le prime 5 righe dell'output atteso:

$ awk -f tst.awk file | head -5
fourth hit third
fourth hit third
fourth hit third
fourth hit third
fourth hit third

ed ecco il risultato quando si esegue il piping ad un secondo script awk per verificare che lo script principale sopra sia effettivamente funzionante esattamente come previsto:

$ awk -f tst.awk file |
awk '!seen[$0]++{unq++;r=$0} END{print ((unq==1) && (seen[r]==1000000) && (r=="fourth hit third")) ? "PASS" : "FAIL"}'
PASS

Ecco i risultati della temporizzazione della 3a esecuzione di gawk 4.1.1 in esecuzione in bash 4.3.33 su cygwin64:

$ time awk -f tst.awk file > /dev/null
real    0m4.711s
user    0m4.555s
sys     0m0.108s

Nota quanto sopra è la terza esecuzione per rimuovere le differenze di memorizzazione nella cache.

Qualcuno può fornire l'equivalente C, perl, python, qualunque sia il codice a questo:

$ cat tst.awk
/hit [[:digit:]]*0 / { print $4, $1, $3 }

vale a dire trovare REGEXP su una linea (non stiamo cercando un'altra soluzione che funzioni attorno all'esigenza di una regexp), dividere la linea in ciascuna serie di spazi contigui e stampare il 4 °, poi il 1 °, poi il 3 ° campo separati da un singolo carattere in bianco?

In tal caso, possiamo testarli tutti su un'unica piattaforma per vedere / registrare le differenze di rendimento.

Il codice ha contribuito finora:

AWK (può essere testato contro gawk, ecc. Ma mawk, nawk e forse altri richiederanno [0-9] invece di [: digit:])

awk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file

PHP

php -R 'if(preg_match("/hit \d*0 /", $argn)){$f=preg_split("/\s+/", $argn); echo $f[3]." ".$f[0]." ".$f[2];}' < file

conchiglia

egrep 'hit [[:digit:]]*0 ' file | awk '{print $4, $1, $3}'
grep --mmap -E "^hit [[:digit:]]*0 " file | awk '{print $4, $1, $3 }'

Rubino

$ cat tst.rb
File.open("file").readlines.each do |line|
  line.gsub(/(hit)\s[0-9]*0\s+(.*?)\s+(.*)/) { puts "#{$3} #{$1} #{$2}" }
end
$ ruby tst.rb

Perl

$ cat tst.pl
#!/usr/bin/perl -nl
# A solution much like the Ruby one but with atomic grouping
print "$4 $1 $3" if /^(hit)(?>\s+)(\d*0)(?>\s+)((?>[^\s]+))(?>\s+)(?>([^\s]+))$/
$ perl tst.pl file

Pitone

none yet

C

none yet

L'applicazione di egrep prima di awk offre una grande accelerazione:

[email protected] ~ % wc -l file
    10000000 file
[email protected] ~ % for i in {1..5}; do time egrep 'hit [[:digit:]]*0 ' file | awk '{print $4, $1, $3}' | wc -l ; done
    1000000
    egrep --color=auto 'hit [[:digit:]]*0 ' file  0.63s user 0.02s system 85% cpu 0.759 total
    awk '{print $4, $1, $3}'  0.70s user 0.01s system 93% cpu 0.760 total
    wc -l  0.00s user 0.02s system 2% cpu 0.760 total
    1000000
    egrep --color=auto 'hit [[:digit:]]*0 ' file  0.65s user 0.01s system 85% cpu 0.770 total
    awk '{print $4, $1, $3}'  0.71s user 0.01s system 93% cpu 0.771 total
    wc -l  0.00s user 0.02s system 2% cpu 0.771 total
    1000000
    egrep --color=auto 'hit [[:digit:]]*0 ' file  0.64s user 0.02s system 82% cpu 0.806 total
    awk '{print $4, $1, $3}'  0.73s user 0.01s system 91% cpu 0.807 total
    wc -l  0.02s user 0.00s system 2% cpu 0.807 total
    1000000
    egrep --color=auto 'hit [[:digit:]]*0 ' file  0.63s user 0.02s system 86% cpu 0.745 total
    awk '{print $4, $1, $3}'  0.69s user 0.01s system 92% cpu 0.746 total
    wc -l  0.00s user 0.02s system 2% cpu 0.746 total
    1000000
    egrep --color=auto 'hit [[:digit:]]*0 ' file  0.62s user 0.02s system 88% cpu 0.727 total
    awk '{print $4, $1, $3}'  0.67s user 0.01s system 93% cpu 0.728 total
    wc -l  0.00s user 0.02s system 2% cpu 0.728 total

contro:

[email protected] ~ % for i in {1..5}; do time gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null; done
    gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  2.46s user 0.04s system 97% cpu 2.548 total
    gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  2.43s user 0.03s system 98% cpu 2.508 total
    gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  2.40s user 0.04s system 98% cpu 2.489 total
    gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  2.38s user 0.04s system 98% cpu 2.463 total
    gawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  2.39s user 0.03s system 98% cpu 2.465 total

'nawk' è ancora più lento!

[email protected] ~ % for i in {1..5}; do time nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null; done                                          
    nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  6.05s user 0.06s system 92% cpu 6.606 total
    nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  6.11s user 0.05s system 96% cpu 6.401 total
    nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  5.78s user 0.04s system 97% cpu 5.975 total
    nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  5.71s user 0.04s system 98% cpu 5.857 total
    nawk '/hit [[:digit:]]*0 / { print $4, $1, $3 }' file > /dev/null  6.34s user 0.05s system 93% cpu 6.855 total

mawk è leggermente più veloce di gawk .

$ time bash -c 'mawk '\''/hit [[:digit:]]*0 / { print $4, $1, $3 }'\'' file | wc -l'
0

real    0m1.160s
user    0m0.484s
sys     0m0.052s

$ time bash -c 'gawk '\''/hit [[:digit:]]*0 / { print $4, $1, $3 }'\'' file | wc -l'
100000

real    0m1.648s
user    0m0.996s
sys     0m0.060s

(Solo 1.000.000 di righe nel mio file di input. I migliori risultati di molti sono stati visualizzati, sebbene fossero abbastanza coerenti).


  • prima idea

    File.open("file").readlines.each do |line|
      line.gsub(/(hit)\s[0-9]*0\s+(.*?)\s+(.*)/) { puts "#{$3} #{$1} #{$2}" }
    end
  • Seconda idea

    File.read("file").scan(/(hit)\s[[:digit:]]*0\s+(.*?)\s+(.*)/) { |f,s,t| puts "#{t} #{f} #{s}" }

Cercando di ottenere qualcosa in grado di confrontare la risposta ho finito per creare un repo github qui . Ogni push a questo repo attiva una build su travis-ci che compone un file markdown spinto a sua volta al ramo gh-pages per aggiornare una pagina web con una vista sui risultati della build.

Chiunque desideri partecipare può bifrare il repository github, aggiungere test e fare una richiesta di pull che unirò al più presto se non interromperà gli altri test.







awk