繰り返し文字生成の実行時間

2024/08/31 19:08 OS別::Linuxその他::シェル
bash で Separator のような、任意の文字を繰り返し出力する方法を探して、見つけたこちら。
https://genzouw.com/entry/2020/10/29/211143/2094/

選択肢が割とあって「お、おう……」となったので「実行速度」で選ぶことにした記事。
シェルスクリプトなので、普段は実行時間なんて気にしないのだけど。

ここで結論を先に書いておくと、ビルトインコマンドである for 文(for *1; do echo -n '='; done)を利用するのが最強のようです。
次点で、print (printf "=%0.s" 0..9)でした。

*1 : i=0; i<10; i++

テスト環境

テスト環境は、以下のような状況です。
$ uname -moi
armv7l unknown GNU/Linux
$
$ bash -version | head -n 1
GNU bash, バージョン 5.1.4(1)-release (arm-unknown-linux-gnueabihf)
$
$ . /etc/os-release
$ echo ${PRETTY_NAME}
Raspbian GNU/Linux 11 (bullseye)

テスト方法

スクリプトにしておいて、シンプルに time で計測します。

作成したスクリプトおよび内容は、以下の通りです。
$ ls -1
test-A.sh
test-B.sh
test-C.sh
test-D.sh
test-E.sh
test-F.sh
test-G.sh
work.sh
$
$ for i in *.sh
do
  echo "### ${i}"
  echo "----------------------------------------------------"
  cat ${i}
  echo "----------------------------------------------------"
  echo ""
done
### test-A.sh
----------------------------------------------------
#!/bin/bash
printf "=%0.s" {0..9}
----------------------------------------------------

### test-B.sh
----------------------------------------------------
#!/bin/bash
yes = | head -n 10 | tr -d '\n'
----------------------------------------------------

### test-C.sh
----------------------------------------------------
#!/bin/bash
seq -s '=' 0 10 | tr -d '[0-9]'
----------------------------------------------------

### test-D.sh
----------------------------------------------------
#!/bin/bash
seq -s '=' 0 10 | sed "s/[0-9]//g"
----------------------------------------------------

### test-E.sh
----------------------------------------------------
#!/bin/bash
for ((i=0; i<10; i++)); do echo -n '='; done
----------------------------------------------------

### test-F.sh
----------------------------------------------------
#!/bin/bash
seq 10 | xargs -i echo -n =
----------------------------------------------------

### test-G.sh
----------------------------------------------------
#!/bin/bash
seq 10 | xargs -i printf "="
----------------------------------------------------

### work.sh
----------------------------------------------------
#!/bin/bash

fnChecker() {
  echo "### ${1}"
  time ./${1}
}

fnChecker test-A.sh
fnChecker test-B.sh
fnChecker test-C.sh
fnChecker test-D.sh
fnChecker test-E.sh
fnChecker test-F.sh
fnChecker test-G.sh
----------------------------------------------------

$

計測

傾向が見えればよいと考えたため、とりあえず1回実行したところが、以下の数値です。
$ ./work.sh
### test-A.sh
==========
real    0m0.016s
user    0m0.013s
sys     0m0.003s
### test-B.sh
==========
real    0m0.028s
user    0m0.020s
sys     0m0.030s
### test-C.sh
==========

real    0m0.023s
user    0m0.024s
sys     0m0.007s
### test-D.sh
==========

real    0m0.028s
user    0m0.009s
sys     0m0.028s
### test-E.sh
==========
real    0m0.014s
user    0m0.001s
sys     0m0.013s
### test-F.sh
==========
real    0m0.082s
user    0m0.026s
sys     0m0.067s
### test-G.sh
==========
real    0m0.080s
user    0m0.036s
sys     0m0.051s
$

結論

for コマンドを使います。

感想

printf コマンド(test-A.sh)もなかなか良い数字なんだけど、ビルトインコマンドの for によるループ(test-E.sh)が強いですね。

その他、普通は bash の予約語なので利用されない time コマンドを使ってみての計測なんかもしてみましたが、メモリ使用率なんかは変化が少ないようです。

wait

time コマンドで計測できる wait の内容は、次の通り(man bash の引用)。
wait の回数、つまりそのプログラムが自発的にコンテキストスイッチされた回数。
例えば、I/O 操作の完了を待っている間などが該当する。
$ type time
time はシェルの予約語です
$
$ which time
/usr/bin/time
$
$ for i in test-*.sh
> do
>    /usr/bin/time -f "\nwait: %w" ./${i}
> done
==========
wait: 1
==========
wait: 8
==========

wait: 7
==========

wait: 5
==========
wait: 1
==========
wait: 31
==========
wait: 36
$
計測した結果は上記の通りですが、printf と for でのループ、待ちなんてほとんど発生してませんね。

その他

あと、こんな感じで他にもいろいろ計測できたりします。

これは、メモリ(プロセス生存中のそのプロセスの resident set size の最大値)。
$ for i in test-*.sh
do
   echo "### ${i}"
   /usr/bin/time -f "\n Memory: %MKB" ./${i}
done
### test-A.sh
==========
 Memory: 2752KB
### test-B.sh
==========
 Memory: 2820KB
### test-C.sh
==========

 Memory: 2820KB
### test-D.sh
==========

 Memory: 2816KB
### test-E.sh
==========
 Memory: 2692KB
### test-F.sh
==========
 Memory: 2868KB
### test-G.sh
==========
 Memory: 2868KB
$
うん。だいたい 3MB 程度ですね。

メジャーページフォルトの回数

外部コマンドでは、メジャーページフォルトの回数を計測も出来たりします。
$ for i in test-*.sh
do
   echo "### ${i}"
   /usr/bin/time -f "\n Page: %F" ./${i}
done
有意な差がないので結果は載せないですが、test-C.sh (seq -s '=' 0 10 | tr -d '[0-9]') はちょこちょこページフォルトが発生している模様。
他のも、合ったりなかったり。
$ for i in test-*.sh
do
   echo "### ${i}"
   /usr/bin/time -f "\n Memory: %MKB" ./${i}
done