files/
.NETファイルには約10,000個のファイルと10,000個の行がありますmetadata.csv
。metadata.csv
ファイルに関する情報が含まれています。各ファイルに関する情報を印刷してからファイルの内容を印刷するシェルスクリプトがあります。
#!/bin/sh
for FILE in `find files/ -type f`
do
ID=`echo $FILE | sed 's/some/thing/'`
cat metadata.csv | awk -v ORS="" -v id=$ID -F "\t" '$1==id {
print "id=\""id"\" Some=\""$2"\" Thing=\""$5"\" "}'
cat $FILE
done
metadata.csv
内容を変数に割り当てると、作業速度が速くなる可能性があると思いますMETADATA
。私は毎回ディスクからファイルを読み取らずにメモリに保存すると思います。
#!/bin/sh
METADATA=`cat metadata.csv`
for FILE in `find files/ -type f`
do
ID=`echo $FILE | sed 's/some/thing/'`
echo "$METADATA" | awk -v ORS="" -v id=$ID -F "\t" '$1==id {
print "id=\""id"\" Some=\""$2"\" Thing=\""$5"\" "}'
cat $FILE
done
しかし、2番目は速くありませんでした。 1回目は約1分間実行され、2回目は2分以上実行されます。
どのように機能し、2番目のスクリプトが速いのではなく遅いのはなぜですか?
編集する:/bin/sh -> 私のシステムでダッシュ
答え1
他の人がベンチマークを再現するのに十分な情報を提供していません。私は自分で作ってみましたが、echo
dashとkshの使い方が少し速く、mkshの使い方もほぼ同じです。違いがあっても、比率は1:2よりはるかに少ないです。明らかに、これはシェル、カーネル、ユーティリティの実装、データファイルの内容を含む多くのものによって異なります。
これら2つの方法の間には確実な勝者はありません。ファイルがキャッシュにあるため、ディスクから読み込むのは実際には費用がかからない。呼び出しにはcat
外部プロセスをフォークするオーバーヘッドがあり、echo
シェルはbultinです。sh
bashを使用する場合、echo
組み込み関数は出力がパイプに入っても引数を一度に1行ずつ印刷します。これは速度低下の原因の1つです。 Dashとkshはこれを行いません。通常、bashよりもパフォーマンスが良いです。
スクリプトで実行できる最適化がたくさんあります。
この方法の明確な最適化は、
cat
リダイレクト()を使用するか、<metadata.csv awk …
それmetadata.csv
をawkにパラメータとして渡すことです。私のテストでは、リダイレクトはリダイレクトよりわずかに速く、echo
リダイレクトとリダイレクトの間に測定可能な違いはありませんでしたawk … metadata.csv
。引用符なしで変数拡張を使用する場合値に特定の文字が含まれていると、真剣に失敗します。、これは分割とグロービングを実行する必要があるため、シェルの追加操作を作成します。変数置換を省略する理由がわからない場合は、必ず二重引用符を使用してください。
find
同様に、特定のファイル名をブロックし、追加の操作が必要な出力を解析しています。標準ソリューションはfind -exec
;を使用していますが、ファイルを処理するためにシェルを起動するには追加の作業が必要なので、より速くてもそうでない場合もあります。- あなたのawkスクリプトは実際のスクリプトで単純化されたと思います。表示されるスクリプトを使用して、CSVファイルの最初の列に正規表現で特殊でない文字のみが含まれていると仮定すると、代わりにsedを試してみることができますが、一般的にはより専門的なツールがあるため、少し高速です。より速く。しかし、測定可能なレベルはこんなに改善されるという保証もありません。
- 設定すると
ID
外部プログラムが呼び出されます。ここで正確に何をするかに応じて、シェル自体の文字列操作構成を使用してこれを行うことができます。通常、それほど高速でも強力でもありませんが、外部プログラムを呼び出す必要はありません。
全体的にこれらのローカル最適化と組み合わせて、私は以下を選択します。
#!/bin/ksh
find files/ -type f -exec sh -c '
for FILE do
ID=${FILE//some/thing}
sed '/^$ID\t/ s/\([^\t]*\)\t\([^\t]*\)\t[^\t]*\t[^\t]*\t\([^\t]*\).*/id="\1" Some="\2" Thing="\3"/' metadata.csv
cat "$FILE"
done' _ {} +
ただし、より高速なアルゴリズムがある可能性があります。各ファイルの完全なメタデータセットを処理しています。特に、ファイルごとに1行だけ一致させると、不要な比較がたくさん生成されます。ファイル名からIDリストを作成し、それをメタデータと比較する方が高速です。テストされていないコード:
#!/bin/ksh
join -j 1 -t $'\t' -o 2.1,2.2,2.5,1.2 \
<(find files/ -type f | sed 's!/some$!/thing\t&!' | sort) \
<(sort metadata.csv) |
awk -F '\t' '{
print "id =\"" $1 "\" Some=\"" $2 "\" Thing=\" $3 "\"";
system("cat \047" $4 "\047"); # Assuming no single quotes in file names
}'