記事を書いた背景
サーバーの運用などしていると昔の担当者が書いたであろうスクリプトやバッチの改善を余儀なくされる場合があります。
過去にやった改善ではインプットとアウトプットを変更せず、処理速度を改善してほしいというものがあり、各処理ごとにどれぐらい時間がかかっているかデバッグしながら効果的なものに対して対策を打ちました。
検討した対策としては、
- for 文の処理を違う表現にしてみる
- for 文で一行ずつ読んでいた表現を read で読み込むように変更する
- expr で計算していた処理を awk で書き直す
- シーケンシャルになっている処理を分散して処理できるように書き直す
ここでは、expr を awk に書き直して状況が改善されたため、それを紹介します。
結論だけ言えば、expr を awk に書き直すだけで5000倍近く速くなりました。
サンプルデータの作成と確認
適当な数字が書かれた1000行のファイルを用意します。
$ for i in `seq 0 999`; do echo $RANDOM >> foo.txt ; done
$ wc -l foo.txt
1000 foo.txt
$ head foo.txt
17376
21053
22754
20430
27812
5486
19801
27289
6166
15766
一行ずつ数値を読み取り合算するスクリプトを書いてみる
bash
# !/bin/bash
date +"%Y/%m/%d %p %I:%M:%S"
FILE="foo.txt"
SUM=0
while read line;
do
SUM=`expr $SUM + $line`
done < $FILE
echo "合計: ${SUM}"
date +"%Y/%m/%d %p %I:%M:%S"
awk
# !/bin/bash
date +"%Y/%m/%d %p %I:%M:%S"
FILE="foo.txt"
awk '{s += $1} END {print "合計: " s}' < $FILE
date +"%Y/%m/%d %p %I:%M:%S"
実行結果1 ##
expr では13秒ぐらいかかる処理が awk で計算したら1秒かからず処理が完了しました。
$ ./bash.sh
2018/06/11 PM 09:15:07
合計: 16637437
2018/06/11 PM 09:15:20
$ ./awk.sh
2018/06/11 PM 09:15:36
合計: 16637437
2018/06/11 PM 09:15:36
実行結果2
本番環境では1000行で済むようなログなどはないと思うので思い切って、10000000行のランダムな数値のファイルを作成し、awk で計算してみました。
$ for i in `seq 0 9999999`; do echo $RANDOM >> hoge.txt ; done;
$ wc -l hoge.txt
10000000 hoge.txt
$ ls -lh hoge.txt
-rw-r--r-- 1 XXXXXX XXXXXX 54M 6 11 20:00 hoge.txt
$ head hoge.txt
21202
1033
21589
5015
19402
31833
14415
15814
26142
32752
$ cat awk2.sh
# !/bin/bash
date +"%Y/%m/%d %p %I:%M:%S"
FILE="hoge.txt"
awk '{s += $1} END {print "合計: " s}' < $FILE
date +"%Y/%m/%d %p %I:%M:%S"
だいたい54MBのファイルに対して7秒ぐらいで処理が完了しました。
昔作られたスクリプトで不用意に expr が使われている場合は awk に処理を書き換えた方が速いかもしれません。
$ ./awk2.sh
2018/06/11 PM 09:47:34
合計: 163856314744
2018/06/11 PM 09:47:41