変数の内容を読むよりもファイルを開く方が速いのはなぜですか?

変数の内容を読むよりもファイルを開く方が速いのはなぜですか?

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/meminfogrepオープン(ディスクI / Oが含まれていないメモリ内の仮想ファイル)を実行するプロセスをフォークして、それを読み取り、正規表現に/proc/meminfo一致させます。

これらの中で最も高価な部分は、プロセスをフォークし、grepユーティリティとそのライブラリの依存関係をロードし、動的接続を実行し、ロケールデータベースを開き、ディスク上の何十ものファイル(たぶんメモリにキャッシュされています)です。 。

これに比べて読書に関する部分は/proc/meminfo些細です。カーネルは情報を生成するのに時間がかかりませんし、grep情報を読むのにも時間がかかりません。

strace -cこれを実行すると、読み取りに使用される1つおよび1つopen()のシステムコールが、起動時に実行される他のタスク(フォーク計算ではない)と比較して何もないことがわかります。read()/proc/meminfogrepstrace -c

存在する:

a=$(</proc/meminfo)

ksh演算子をサポートするほとんどのシェルでは、$(<...)シェルは単にファイルを開き、その内容を読み取ります(そして末尾の改行を削除します)。bash違いは、読み取り操作を実行するためにプロセスを分岐し、データを親プロセスにパイプするため、はるかに効率が悪いことです。しかし、ここでは一度やってみたので構いません。

存在する:

printf '%s\n' "$a" | grep '^MemFree'

シェルを作成する必要があります二つ同時に実行されますが、パイプを介して互いに対話するプロセスです。パイプの作成、解体、書き込み、読み込みにかかる費用は少ない。より大きなコストは、追加のプロセスを作成することです。プロセススケジュールもある程度影響します。

zsh<<<演算子を使用すると、少し速くなる可能性があります。

grep '^MemFree' <<< "$a"

zshとbashは$a一時ファイルに内容を書き込むことによってこれを行います。これは追加のプロセスを作成するよりも安価ですが、データを直接インポートすることに比べて利点はありません/proc/meminfo。これは/proc/meminfo繰り返すたびに一時ファイルの書き込みが実行されるため、ディスクへのコピー方法よりも効率が低くなります。

dashHere-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秒しかかかりません。

関連情報