昨日の記事「シェルで回答してみる〜」の補足

毛利です。

シェルで回答してみる「シェル操作課題 (cut, sort, uniq などで集計を行う) 設問編」 - pmekyky385の日記

「高速なコマンドを使う」って自分で書いてて全く検証しておらず、気になったので少しだけ。
(見直すと「シェルで回答」ってなんか違和感あるな。UNIXコマンドで回答、かな?(この場合「UNIX」ってつけていいのか分からない))

アクセスログとかだとかなり大きめのファイルになるはずなので。

※後で気づきましたが、Time Machineを停止するとか、そこまで気を使ってません。でも、少なくとも問2の検証では走ってないはず(問8は時間がかかって放置したので走ってるかも。。)

timeで処理時間を検証「問2 このファイルからサーバー名とアクセス先だけ表示しろ」

$ cut -d, -f1,4 data.csv 
$ awk -F, '{printf("%s,%s\n",$1,$4)}' data.csv 
$ sed -e 's/^\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)$/\1,\4/' data.csv 
$ perl -F, -a -nle 'print join(",",$F[0],$F[3])' data.csv 

単純にtimeを使う。

まず、数行しかないdata.csv。結果は表示しない(> /dev/null)
$ wc -l data.csv 
       9 data.csv
$ time cut -d, -f1,4 data.csv > /dev/null

real    0m0.002s
user    0m0.001s
sys     0m0.001s
$ time awk -F, '{printf("%s,%s\n",$1,$4)}' data.csv > /dev/null

real    0m0.003s
user    0m0.001s
sys     0m0.002s
$ time sed -e 's/^\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)$/\1,\4/' data.csv  > /dev/null

real    0m0.003s
user    0m0.001s
sys     0m0.002s
$ time perl -F, -a -nle 'print join(",",$F[0],$F[3])' data.csv > /dev/null

real    0m0.168s
user    0m0.004s
sys     0m0.025s

意外にsedawkが早いという印象。
perlは遅いが、データが増えるとどうなるか。

data.csvを1GBぐらいのファイルに膨らませて検証

300byteぐらいだったので、1GB「ぐらい」になるまで繰り返し。

 ls -l data.csv 
-rw-r--r--  1 xxxxxxxxxxx  wheel  302 12  5 11:46 data.csv
$ time for i in $(seq 1 3333333) ; do cat data.csv >> bigdata.csv ; done
^C
# 終わらない…。一旦停止
$ ls -l bigdata.csv 
-rw-r--r--  1 xxxxxxxxxxx  wheel  108440952 12  7 00:54 bigdata.csv
$ cp bigdata.csv bigdata.csv.100mb
$ cat /dev/null > bigdata.csv
$  time for i in $(seq 1 10) ; do cat bigdata.csv.100mb >> bigdata.csv ; done

real	0m15.735s
user	0m0.074s
sys	0m2.480s
$ du -sh bigdata.csv
1.0G    bigdata.csv

まあ、ざっくり1GBということ。

検証結果

$ time cut -d, -f1,4 bigdata.csv > /dev/null

real	0m45.586s
user	0m44.980s
sys	0m0.572s
$ time awk -F, '{printf("%s,%s\n",$1,$4)}' bigdata.csv > /dev/null

real	0m49.794s
user	0m49.062s
sys	0m0.697s
$ time sed -e 's/^\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)$/\1,\4/' bigdata.csv  > /dev/null

real	1m55.168s
user	1m53.997s
sys	0m0.917s
$ time perl -F, -a -nle 'print join(",",$F[0],$F[3])' bigdata.csv > /dev/null

real	2m4.089s
user	2m2.769s
sys	0m1.122s

こういう単純なものはシンプルが早いか。

timeで処理時間を検証「問8 このログのアクセス先ごとにアクセス数を数え上位1つを表示しろ」

回答したものと、人様のperlを使ったワンライナー回答を比べてみる。

$ time cut -d, -f4 bigdata.csv | sort | uniq -c | sort -k1n | tail -1
14363040 /profile.php

real	14m13.132s
user	14m42.638s
sys	0m4.538s
$ time perl -aF, -nle '$m{$F[3]}++;eof&&print join" ",@{(sort{$b->[0]<=>$a->[0]}map{[$m{$_},$_]}keys%m)[0]}' bigdata.csv
14363040 /profile.php

real	1m57.723s
user	1m56.765s
sys	0m0.867s

perlはやっ!!


いじょ。