私のスクリプトに問題がありますか?それとも、BashはPythonよりはるかに遅いですか?

私のスクリプトに問題がありますか?それとも、BashはPythonよりはるかに遅いですか?

ループを10億回実行して、BashとPythonの速度をテストしました。

$ cat python.py
#!/bin/python
# python v3.5
i=0;
while i<=1000000000:
    i=i+1;

クンコード:

$ cat bash2.sh
#!/bin/bash
# bash v4.3
i=0
while [[ $i -le 1000000000 ]]
do
let i++
done

このtimeコマンドを使用すると、Pythonコードは完了するのに48秒しかかかりませんが、Bashコードはスクリプトを終了するのに1時間以上かかることを発見しました。

なぜですか? Bashが速くなると期待しています。私のスクリプトに問題がありますか?または、このスクリプトを使用すると、Bashは本当にはるかに遅くなりますか?

答え1

シェルループは遅く、bashのループは最も遅いです。 Shellは、ループ内で重い操作を実行するのには適していません。シェルは、一括データに対していくつかの外部最適化プロセスを開始するように設計されています。


とにかく、シェルループがどのように比較されるのか疑問に思っていくつかのベンチマークを実行しました。

#!/bin/bash

export IT=$((10**6))

echo POSIX:
for sh in dash bash ksh zsh; do
    TIMEFORMAT="%RR %UU %SS $sh"
    time $sh -c 'i=0; while [ "$IT" -gt "$i" ]; do i=$((i+1)); done'
done


echo C-LIKE:
for sh in bash ksh zsh; do
    TIMEFORMAT="%RR %UU %SS $sh"
    time $sh -c 'for ((i=0;i<IT;i++)); do :; done'
done

G=$((10**9))
TIMEFORMAT="%RR %UU %SS 1000*C"
echo 'int main(){ int i,sum; for(i=0;i<IT;i++) sum+=i; printf("%d\n", sum); return 0; }' |
   gcc -include stdio.h -O3 -x c -DIT=$G - 
time ./a.out

詳細:

  • CPU: Intel(R) Core(TM) i5 CPU M 430 @ 2.27GHz
  • ksh: sh バージョン (AT&T Research) 93u+ 2012-08-01
  • bash:GNU bash、バージョン4.3.11(1)-リリース(x86_64-pc-linux-gnu)
  • zsh: zsh 5.2 (x86_64-unknown-linux-gnu)
  • ダッシュ:0.5.7-4ubuntu1

)

(短縮)結果(繰り返しあたりの時間)は次のとおりです。

POSIX:
5.8 µs  dash
8.5 µs ksh
14.6 µs zsh
22.6 µs bash

C-LIKE:
2.7 µs ksh
5.8 µs zsh
11.7 µs bash

C:
0.4 ns C

結果から:

少し速いシェルループが必要な場合は[[構文があり、高速シェルループが必要な場合は高度なシェルにあり、Cに似たforループもあります。次に、C言語のforループを使用します。while [同じシェルの-loopより約2倍高速です。

  • ケシ最速のfor (ループはおおよそ2.7マイクロ秒各繰り返し
  • スプリント最速のwhile [ループはおおよそ5.8マイクロ秒各繰り返し

C for ループは 10 進数 3 ~ 4 倍速くなります。 (TorvaldsがCが好きだと聞きました。)

最適化されたC forループwhile [は、bashループ(最も遅いシェルループ)より56500倍速く、for (kshループ(最も速いシェルループ)より6750倍速いです。


繰り返しますが、シェルの一般的なモードは、外部オプティマイザのいくつかのプロセスでロードをオフロードすることであるため、シェルが遅くなることは重要ではありません。

このパターンを使用すると、シェルでPythonスクリプトよりもパフォーマンスの良いスクリプトを簡単に作成できることがよくあります。

考慮すべきもう一つのことは開始時間です。

time python3 -c ' '

私のPCでは30〜40msかかり、シェルは約3msかかります。多くのスクリプトを起動すると、この時間はすばやく合計され、Pythonの起動にかかる追加の27〜37ミリ秒以内に多くのことができます。この間、小さなスクリプトを複数回完了できます。

(NodeJは起動に約100ミリ秒しかかからないので、おそらくこの部門で最悪のスクリプトランタイムになるでしょう。

答え2

これはbashの既知のバグです。マニュアルページを参照して「BUGS」を検索してください。

BUGS
       It's too big and too slow.

;)


シェルスクリプトと他のプログラミング言語との間の概念的な違いについての優れた入門書については、以下を読むことを強くお勧めします。

最も関連性の高い抜粋:

シェルは高級言語です。言語ではないと言うこともできます。すべてのコマンドラインソルバーよりも優先されます。操作はユーザーが実行するコマンドによって実行され、シェルはコマンドを調整します。

...

IOW、シェルでは、特にテキストを処理するときに何千ものツールを順番に実行し、各ツールが起動、実行、クリーンアップされるのを待つのではなく、できるだけ少ないユーティリティを呼び出して作業に適しています。次のツールをもう一度実行してください。

...

前述したように、コマンドを実行するにはコストがかかります。命令が組み込まれていないとコストは膨大ですが、組み込まれていてもコストは途方もなくなります。

シェルはこのように動作するようには設計されておらず、高性能プログラミング言語であるとも主張していません。彼らはそうではありません。彼らは単にコマンドラインソルバーです。したがって、これに関して最適化はほとんど行われなかった。


シェルスクリプトでは大きなループを使用しないでください。

答え3

いくつかのテストを実行し、私のシステムで次のコードを実行しました。どちらも競争力を持たせるのに必要な程度のスピードアップを達成できなかったが、より速くすることができる。

テスト1:18.233秒

#!/bin/bash
i=0
while [[ $i -le 4000000 ]]
do
    let i++
done

テスト2:20.45秒

#!/bin/bash
i=0
while [[ $i -le 4000000 ]]
do 
    i=$(($i+1))
done

テスト3:17.64秒

#!/bin/bash
i=0
while [[ $i -le 4000000 ]]; do let i++; done

テスト4:26.69秒

#!/bin/bash
i=0
while [ $i -le 4000000 ]; do let i++; done

テスト5:12.79秒

#!/bin/bash
export LC_ALL=C

for ((i=0; i != 4000000; i++)) { 
:
}

最後の項目の重要な部分はLC_ALL = Cエクスポートです。私はこれを使用すると、多くのbash操作、特に正規表現機能がはるかに高速になることがわかりました。また、{} と : をランダムに使用する文書化されていない構文も示しています。

答え4

A:BashはPythonよりはるかに遅いです。

ブログ投稿に小さな例があります。多言語パフォーマンス

関連情報