bash, ksh, zsh の速度比較 - 拡張 POSIX シェルスクリプト Advent Calendar 2013 - ダメ出し Blog

2013-12-01(Sun) [sh][shell][benchmark] [更新履歴]

拡張 POSIX シェルスクリプト Advent Calendar 2013、 1日目の記事です。

「このくらいの要件ならシェルで書ける!」と意気込んで実装を開始して、 たとえやりきっても「あー、やっぱり (あなたの好きな LL) で書けばよかった」 なんてことありませんか? 私はあまりありません。 POSIX シェルやそれ以前の非力なシェルはともかく、bash や ksh、zsh のような拡張 POSIX シェルであれば、少し無理をすれば、大抵の処理は書けます。 老害ですね、わかります。

しかし実装は問題ないのですが、速度がいまひとつなのはどうしても否めません。 そこで一つ、少しでも実行速度を改善する簡単な方法を伝授しましょう。 それは「bash は避ける」です。

過去の経験から bash が遅いということに気付いていたのですが、 今回はベンチマークを取ることで検証してみました。

環境:

$ grep 'model name' /proc/cpuinfo |head -n1
model name      : Intel(R) Core(TM) i7-2620M CPU @ 2.70GHz
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux unstable (sid)
Release:        unstable
Codename:       sid
$ bash --version |head -n1
GNU bash, バージョン 4.2.45(1)-release (x86_64-pc-linux-gnu)
$ ksh --version
  version         sh (AT&T Research) 93u+ 2012-08-01
$ zsh --version
zsh 5.0.2 (x86_64-pc-linux-gnu)

結果: 各数値は「秒数 (bash比)」です。

$ zsh ./sh-benchmark.zsh
| bash           | ksh            | zsh            |
|  0.32 ( 100.0) |  0.06 (  18.9) |  0.05 (  16.0) | Parameter Expansion 1: "$PARAMETER"
|  0.91 ( 100.0) |  0.11 (  12.2) |  0.10 (  11.0) | Parameter Expansion 2: $PARAMETER
|  4.61 ( 100.0) |  2.34 (  50.8) |  3.08 (  67.0) | Parameter Expansion 3: "${PARAMETER##*/}" (modifier)
|  5.43 ( 100.0) |  0.10 (   1.8) | 78.74 (1449.0) | Array Parameter Expansion 1: "${ARRAY[1]}" (one element)
|  7.08 ( 100.0) |  0.63 (   8.9) | 11.12 ( 157.2) | Array Parameter Expansion 2: "${ARRAY[@]}" (all elements)
| 13.19 ( 100.0) | 11.37 (  86.2) |  6.31 (  47.8) | Arithmetic Evaluation 1: let EXPRESSION
|  9.32 ( 100.0) |  4.65 (  49.9) |  3.33 (  35.8) | Arithmetic Evaluation 2: ((EXPRESSION))
| 13.02 ( 100.0) |  9.97 (  76.6) |  4.82 (  37.0) | Arithmetic Expansion 1: $((EXPRESSION))
| 16.67 ( 100.0) | 10.86 (  65.2) |  5.31 (  31.9) | Arithmetic Expansion 2: $(($PARAMETER+EXPRESSION))
|  6.53 ( 100.0) |  3.50 (  53.6) |  2.38 (  36.5) | Test 1: [[ EXPRESSION ]]
| 16.13 ( 100.0) |  4.20 (  26.0) | 10.30 (  63.8) | Test 2: [ EXPRESSION ]
|  6.65 ( 100.0) | 38.53 ( 579.3) |  5.63 (  84.6) | Fork
| 13.76 ( 100.0) |  7.96 (  57.8) | 14.48 ( 105.2) | Fork & Exec
|  2.91 ( 100.0) |  0.21 (   7.2) |  1.77 (  60.9) | Iterate Parameters 1; for
| 29.57 ( 100.0) |  0.08 (   0.3) |  5.18 (  17.5) | Iterate Parameters 2: while shift
| 72.66 ( 100.0) |  0.19 (   0.3) |  5.65 (   7.8) | Iterate Parameters 3: while ((n++<$#))

ksh は fork 以外 (プロセス置換以外かも)、zsh は配列パラメーターの展開以外、 bash より高速であることがわかりました。

各ベンチマークの内容: sh-benchmark-scripts.sh

変なところに bash, ksh, zsh の違いがあったり、 {1..1000000} の右辺を増やしすぎると ksh が Memory fault と言って死んだり、 意外と作るのに苦労しました。

ベンチマークの前処理で $(zsh -c "echo {1..1000000}") のように一部 zsh を利用していますが、これは今回利用した bash 4.2 に {1..1000000} に絡む処理をやらせた場合に、異様に遅い問題があったためです。 たとえば次の例を実行したとき数分待っても終わらないんですが。 bash 3.0, 3.2, 4.1 では数秒で終了します。

$ time bash -c 'i=( {1..1000000} );'

ベンチマークスクリプト: sh-benchmark.zsh


ところで、12月25日はクリスマスな上に、 OSS 界隈で地味に活躍されているふみやすさんの誕生日ですね。
http://www.amazon.co.jp/registry/wishlist/27M7TV8CEEF6G?sort=priority

逆に、あなたの書いた OSS や Blog や Advent Calendar が気に入ったら何か送りたく なってしまうかもしれないので、プロフィールや Web サイトに あなたの Amazon 欲しいものリストの URL を貼っておいてくださいね!


私が勤める OSSTech っていう某弊社で社員募集しているようです。 人材紹介会社を介さなければ、入社後に 20万円のボーナス! 「ふみやすっていう人に紹介された」と言ってもらえると私にもボーナス!! → http://www.osstech.co.jp/company/recruit

バツイチでアラフォーでほとんど諦めていますが、新たなパートナーも募集中です。 ちゃんと愛情表現してくれて精神的に自立している女性がいいです。お友達から始めましょう。 → https://twitter.com/satoh_fumiyasu


よろしければ、過去の Advent Calendar もどうぞ。