bash
スクリプトには/proc/
ファイルのさまざまな値が必要です。これまで、次のように直接grepファイルが数十行あります。
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
効率のために、ファイルの内容を変数とgrepに保存します。
a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'
ファイルを複数回開くのではなく、一度だけ開き、変数の内容を把握する必要があります。これは速いと思いましたが、実際には遅いです。
bash 4.4.19 $ time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real 0m0.803s
user 0m0.619s
sys 0m0.232s
bash 4.4.19 $ a=$(</proc/meminfo)
bash 4.4.19 $ time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real 0m1.182s
user 0m1.425s
sys 0m0.506s
dash
そして江戸同様ですzsh
。ファイルの特殊な状態が/proc/
原因ではないかと疑われましたが、内容を/proc/meminfo
通常のファイルにコピーして使用してみると次のようになります。
bash 4.4.19 $ cat </proc/meminfo >meminfo
bash 4.4.19 $ time for i in $(seq 1 1000);do grep ^MemFree meminfo; done >/dev/null
real 0m0.790s
user 0m0.608s
sys 0m0.227s
ここで文字列を使用してパイプを保存すると、少し速くなりますが、それでもファイルを使用するのと同じくらい高速ではありません。
bash 4.4.19 $ time for i in $(seq 1 1000);do <<<"$a" grep ^MemFree; done >/dev/null
real 0m0.977s
user 0m0.758s
sys 0m0.268s
変数から同じ内容を読むよりもファイルを開く方が速いのはなぜですか?
答え1
ここの話じゃないファイルを開く比較的変数の内容を読むただし、追加のプロセスをフォークするかどうかがより重要です。
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
grep
オープン(ディスクI / Oが含まれていないメモリ内の仮想ファイル)を実行するプロセスをフォークして、それを読み取り、正規表現に/proc/meminfo
一致させます。
これらの中で最も高価な部分は、プロセスをフォークし、grepユーティリティとそのライブラリの依存関係をロードし、動的接続を実行し、ロケールデータベースを開き、ディスク上の何十ものファイル(たぶんメモリにキャッシュされています)です。 。
これに比べて読書に関する部分は/proc/meminfo
些細です。カーネルは情報を生成するのに時間がかかりませんし、grep
情報を読むのにも時間がかかりません。
strace -c
これを実行すると、読み取りに使用される1つおよび1つopen()
のシステムコールが、起動時に実行される他のタスク(フォーク計算ではない)と比較して何もないことがわかります。read()
/proc/meminfo
grep
strace -c
存在する:
a=$(</proc/meminfo)
ksh演算子をサポートするほとんどのシェルでは、$(<...)
シェルは単にファイルを開き、その内容を読み取ります(そして末尾の改行を削除します)。bash
違いは、読み取り操作を実行するためにプロセスを分岐し、データを親プロセスにパイプするため、はるかに効率が悪いことです。しかし、ここでは一度やってみたので構いません。
存在する:
printf '%s\n' "$a" | grep '^MemFree'
シェルを作成する必要があります二つ同時に実行されますが、パイプを介して互いに対話するプロセスです。パイプの作成、解体、書き込み、読み込みにかかる費用は少ない。より大きなコストは、追加のプロセスを作成することです。プロセススケジュールもある程度影響します。
zsh<<<
演算子を使用すると、少し速くなる可能性があります。
grep '^MemFree' <<< "$a"
zshとbashは$a
一時ファイルに内容を書き込むことによってこれを行います。これは追加のプロセスを作成するよりも安価ですが、データを直接インポートすることに比べて利点はありません/proc/meminfo
。これは/proc/meminfo
繰り返すたびに一時ファイルの書き込みが実行されるため、ディスクへのコピー方法よりも効率が低くなります。
dash
Here-stringはサポートされていませんが、そのheredocは追加のプロセス生成を含まないパイプを使用して実装されています。存在する:
grep '^MemFree' << EOF
$a
EOF
シェルはパイプを作成し、プロセスを分岐します。子プロセスはgrep
パイプの読み取り端としてstdinを実行し、親プロセスはパイプのもう一方の端に書き込みます。
ただし、配管とプロセスの同期は、データを直接インポートするよりもコストがかかります/proc/meminfo
。
内容/proc/meminfo
も短く、製作期間も長くありません。一部のCPUサイクルを節約するには、プロセスの分岐や外部コマンドの実行など、高価な部分を排除する必要があります。
良い:
IFS= read -rd '' meminfo < /proc/meminfo
memfree=${meminfo#*MemFree:}
memfree=${memfree%%$'\n'*}
memfree=${memfree#"${memfree%%[! ]*}"}
bash
パターンマッチング効率が非常に低い状況を避けてください。を使用すると、zsh -o extendedglob
次のように短縮できます。
memfree=${${"$(</proc/meminfo)"##*MemFree: #}%%$'\n'*}
これは^
多くのシェルで特別であることに注意してください(Bourne、fish、rc、es、zshには少なくともExtendedglobオプションがあります)。引用することをお勧めします。また、echo
任意のデータを出力するために使用することができないので、printf
上記の方法を使用しました。
答え2
最初のケースでは、grepユーティリティを使用してファイル内の内容を見つけます/proc/meminfo
。 file は/proc
仮想ファイルシステムなので、/proc/meminfo
ファイルはメモリ内にあり、コンテンツを取得するのに時間がかかりません。
しかし、2番目のケースでは、パイプを作成し、そのパイプを使用して最初のコマンドの出力を2番目のコマンドに渡すため、コストがかかります。
違いは/proc
(メモリにあるため)パイプです。以下の例をご覧ください。
time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real 0m0.914s
user 0m0.032s
sys 0m0.148s
cat /proc/meminfo > file
time for i in {1..1000};do grep ^MemFree file;done >/dev/null
real 0m0.938s
user 0m0.032s
sys 0m0.152s
time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real 0m1.016s
user 0m0.040s
sys 0m0.232s
答え3
あなたは電話中です外部どちらの場合もコマンド(grep)です。外部呼び出しにはサブシェルが必要です。シェルをフォークすることが遅延の根本原因です。どちらの場合も似ているため、待ち時間も似ています。
外部ファイルを一度だけ読み込み、変数で複数回使用する場合は、シェルを終了しないでください。
meminfo=$(< /dev/meminfo)
time for i in {1..1000};do
[[ $meminfo =~ MemFree:\ *([0-9]*)\ *.B ]]
printf '%s\n' "${BASH_REMATCH[1]}"
done
grep呼び出しには1秒ではなく約0.1秒しかかかりません。