【時間計測】シェルスクリプトで1から100000まで表示する

Shell Script

こんにちは、山田ハヤオです。

プログラミングの入門のときに、よく「for文を使って1~10を表示しよう」みたいなのありますよね。

あれを何気なくシェルスクリプトでやってみようと思ったら案外いろんな方法がありました。

それをツイートしたのが以下

すると鍵垢で山Dが

と言ってきたのでベンチを回してみようと思います。

スポンサーリンク

前提について

10000じゃつまらないので(?)1から100000までを出力させます。

処理の計測は処理を書いたシェルスクリプトをtimeコマンドで呼び出して計測します。

文字出力は全てBashの組み込みコマンドであるechoを使用して行います。(printfのほうが速いのかな?)

また、計算などの方法も、それぞれのループに合わせて最適なものを選んでいます。

(本当は処理は全て統一したほうが正しい結果が出るんでしょうけど)

/usr/bin/echoは使いません(何が違うんでしょうかね…)

また、あくまでもシェルスクリプトなのでコンパイル言語は無しとします。

使用したBashのバージョンとPCのスペックを載せておきます。

$ LANG=C bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

forを使う方法

シェルスクリプトのforの基本構文をそのまま使います。

今回はbashのブレース展開と併用してnumber変数に1つづつ代入してechoしてもらいます。

改行がなければecho {1..100000}

#!/usr/bin/env bash
number=0
for number in {1..100000}; do
    echo ${number}
done

whileを使う方法

whileはコマンドがtrueの間だけ処理を実行するコマンドです。

今回はtestコマンドと併用して「numberが10でない間」だけ文字を出力して1を足してもらいます。

testコマンドってtest[[[ってありますけど速度に差はあるんでしょうか?

まぁ今回は一番良く使う[[を使用します。

#!/usr/bin/env bash
number=0
while [[ ! ${number} == "100000" ]]; do
    number=$(( number + 1))
    echo ${number}
done

関数を使ったループ

自分が「何か普通じゃない方法でループできないだろうか」と考えた結果がこれです。

関数を再帰的に呼び出してループを行い、10に達したら関数を終了させます。

#!/usr/bin/env bash
number=0
count() {
    if [[ "${number}" == "100000" ]]; then
        return
    else
        number=$(( number + 1 ))
        echo $number
    fi
    count
}
count

Pythonを使う

正直この方法は思いつきませんでした()

mk-linuxさんのコードをそのままコピペしてます。

#!/usr/bin/env bash
echo 1 100000 | python3 -c 'import sys; list = sys.stdin.readline().split(); print("\n".join([ str(i) for i in range(int(list[0]), int(list[1])+1)]))'

PHPを使う その1

この企画で初めて知ったんですがphpコマンドって-rを使うと直接コマンドラインからPHPを実行できるんですよね。それを利用した方法です。

PHPを使うという発想がまったく無かったのでstmkzaさんとnaokoさんに感謝です。

(ターミナルからシェルとして実行するためにいろいろ苦労しました)

#!/usr/bin/env bash
php -r '$a=$argv;$s=$a[1];for(;$s<=$a[2];$s++)echo "$s\n";' 1 100000

PHPを使う その2

全く別の処理らしいです。stmkzaさんありがとうございます。

#!/usr/bin/env bash
php -r 'echo implode("\n",range($argv[1],$argv[2]));' 1 100000

JavaScriptを使う

JavaScriptってコマンドラインで実行できるんですね…初めて知りました

kokkiemouseさんありがとうございましたm(_ _)m

#!/usr/bin/env bash
js68 -e 'for(let i=1;i < 100001;i++){ console.log(i);}'

seqコマンドを使う方法

もはやループじゃないです。もうみんなこれ使いましょうよ。絶対これ一番はy(ry

一応おまけとして測りましょうかね()

#!/usr/bin/env bash
seq 1 100000

バカ正直にechoする

もう何も考えないで頭悪くechoします。

でも100000まで全部ちまちま書くほど暇じゃないのでコマンドでスクリプトを生成します(は?)

今回は準備なので下のコマンドの実行時間は含めません。あくまで生成されたスクリプトの時間です。

下のコマンドを考えるのが今回で一番むずかしかった部分です()

以下のコマンドはターミナルから直接実行します。

(echo -ne '#!/usr/bin/env bash\necho -e "'; for i in $(seq 1 100000); do if [[ "${i}" == "100000" ]]; then echo -n "${i}"; else echo -n "${i}\\n"; fi; done; echo "\"" ) > echo.sh

頭悪いスクリプトを作るために頭を使いました。

こんなツイートも見かけたのでありだと思います()

ソースコード

ソースコードはアーカイブファイルにまとめて置いておきます。ライセンスはWTFPLです。

https://山d.com/storage/misc/bash-loop.tar.xz

毎度のごとく山D氏のサーバを借ります()

MD5 d09e9548929800f4c6195751a1dc873d

SHA1 9cf9e6fe0a9811d844d15b77358e16b57769cf0a

検証タイム!

ソースコードの紹介が終わったので今から実際に測定をしようと思います。

測定はtimeコマンドを使用します。

forを使う

$ time ./for.sh

1
.
100000

real 0m0.748s
user 0m0.526s
sys  0m0.218s

whileを使う

$ time ./while.sh
1
.
100000

real 0m1.267s
user 0m1.036s
sys  0m0.225s

関数を使う

まさかの異常終了でした。何重にも呼びすぎて落ちたのかな…

1から100までテストした時はうまく言ったんです()

どうにか継続させる方法があったら教えて下さい。

$ time ./function.sh
1
.
8315
Segmentation fault (コアダンプ)

real 0m9.008s
user 0m8.381s
sys  0m0.106s

Pythonを使う

$ time ./python.sh
1
.
100000

real 0m0.151s
user 0m0.043s
sys  0m0.052s

PHPを使う その1

$ time ./php1.sh
1
.
100000

real 0m0.420s
user 0m0.081s
sys  0m0.214s

PHPを使う その2

$ time ./php2.sh
1
.
100000
real 0m0.145s
user 0m0.018s
sys  0m0.059s

JavaScriptを使う

$ time ./js.sh
1
.
100000

real 0m0.453s
user 0m0.133s
sys  0m0.233s

seqを使う

$ time ./seq.sh
1
.
100000

real 0m0.142s
user 0m0.009s
sys  0m0.057s

echoする

$ time ./echo.sh
1
.
100000

real 0m0.359s
user 0m0.116s
sys  0m0.202s

ランキング付けをしよう

いろいろ考えるのは面倒なのでrealでランキング付けをしてみます。

順位コマンド説明時間
1seq.shseq 1 1000000m0.142s
2php2.shPHPその20m0.145s
3python.shPythonでforを使う0m0.151s
4echo.shechoに全部書いた0m0.359s
5php1.shPHP その10m0.420s
6js.shJavaScriptでforを使う0m0.453s
7for.shBashでforを使う0m0.748s
8while.shBashでwhileを使う0m1.267s
9function.shBashで関数を使う(異常終了)0m9.008s

seqが当然最速なのはまだしも、PHPがほぼ誤差レベルなのでかなり驚きました。

速いと予想してたJavaScriptが遅いのには少しショックです

一番苦労したechoはシェルスクリプトの中では好成績を残しました。(seqは入れないで)

やっぱりシェルスクリプトってくっそ遅いんだなぁって実感しました。

あとがき

シェルスクリプトのループを比較するつもりがいつのまにか他言語を使って処理してました()

PHPやPython、JSなどのコードを提供してくれた方々には本当に感謝です。

正直途中から何やってるのかわからなくなりました。何の意味があるんだこれ

まぁもしseqが使えなくてPythonやPHPが使える時はPHPでやると良いという結果ですね(やけくそ)


もし他のも「俺のコードのほうが速いぞ!」とか「こっちの言語のほうが速い!」という方はぜひコメントにお願いします。コンパイル言語は無しですよ!

コメント

タイトルとURLをコピーしました