大きなファイルから特定の1行をフェッチする必要があります(1500000 lines)、複数のファイルをループして複数回、私は自分に何が最良のオプションかを尋ねていました(inパフォーマンスの条件)。これを行うには多くの方法があります。私はこれらを2つ使用します
cat ${file} | head -1
または
cat ${file} | sed -n '1p'
私はこれに対する答えを見つけることができませんでした。どちらも最初の行または2つのうちの1つだけをフェッチします(または両方)最初にファイル全体を開いてから、行1をフェッチしますか?
cat
の無用な使用をやめて、次のようにします。
$ sed -n '1{p;q}' file
これにより、行が出力された後、sed
スクリプトが終了します。
ベンチマークスクリプト:
#!/bin/bash
TIMEFORMAT='%3R'
n=25
heading=('head -1 file' 'sed -n 1p file' "sed -n '1{p;q} file" 'read line < file && echo $line')
# files upto a hundred million lines (if your on slow machine decrease!!)
for (( j=1; j<=100,000,000;j=j*10 ))
do
echo "Lines in file: $j"
# create file containing j lines
seq 1 $j > file
# initial read of file
cat file > /dev/null
for comm in {0..3}
do
avg=0
echo
echo ${heading[$comm]}
for (( i=1; i<=$n; i++ ))
do
case $comm in
0)
t=$( { time head -1 file > /dev/null; } 2>&1);;
1)
t=$( { time sed -n 1p file > /dev/null; } 2>&1);;
2)
t=$( { time sed '1{p;q}' file > /dev/null; } 2>&1);;
3)
t=$( { time read line < file && echo $line > /dev/null; } 2>&1);;
esac
avg=$avg+$t
done
echo "scale=3;($avg)/$n" | bc
done
done
benchmark.sh
として保存し、bash benchmark.sh
を実行するだけです。
結果:
head -1 file
.001
sed -n 1p file
.048
sed -n '1{p;q} file
.002
read line < file && echo $line
0
** 1,000,000行のファイルからの結果。*
したがって、sed -n 1p
の時間はファイルの長さとともに直線的に増加しますが、他のバリエーションのタイミングは一定です(そして無視できます)最初の行を読んだ後、すべて終了したので:
注:より高速なLinuxボックス上にあるため、タイミングは元の投稿とは異なります。
本当に最初の行を取得して数百のファイルを読み取っているだけの場合は、外部の外部コマンドの代わりにシェルのビルトインを検討してください。bashとkshのシェルビルトインであるread
を使用してください。これにより、awk
、sed
、head
などによるプロセス作成のオーバーヘッドがなくなります。
もう1つの問題は、I/Oの時間指定パフォーマンス分析です。初めてファイルを開いて次に読み取るとき、ファイルデータはおそらくメモリにキャッシュされません。ただし、同じファイルで2番目のコマンドをもう一度実行すると、データとiノードがキャッシュされているため、使用したコマンドに関係なく、時間指定された結果の方が高速になる可能性があります。さらに、inodeは事実上永久にキャッシュされたままになる可能性があります。たとえば、Solarisの場合です。とにかく、数日。
たとえば、linuxはすべてとキッチンシンクをキャッシュします。これは優れたパフォーマンス属性です。ただし、問題を認識していない場合、ベンチマークは問題になります。
このキャッシュ効果「干渉」はすべて、OSとハードウェアの両方に依存します。
したがって、1つのファイルを選択し、コマンドで読み取ります。現在はキャッシュされています。同じテストコマンドを数十回実行します。これは、I/Oハードウェアではなく、コマンドと子プロセスの作成の影響をサンプリングしています。
これは、ファイルを1回読み取った後、同じファイルの最初の行を取得する10回の繰り返しに対して、sedとreadの比較です。
sed:sed '1{p;q}' uopgenl20121216.lis
real 0m0.917s
user 0m0.258s
sys 0m0.492s
読んだ: read foo < uopgenl20121216.lis ; export foo; echo "$foo"
real 0m0.017s
user 0m0.000s
sys 0m0.015s
これは明らかに不自然ですが、組み込みのパフォーマンスとコマンドの使用の違いを示しています。
パイプを避けるのはどうですか? sed
とhead
はどちらも、引数としてファイル名をサポートしています。このようにして、猫のそばを通り過ぎることを避けます。私はそれを測定しませんでしたが、N行後に計算を停止するため、大きなファイルではヘッドが高速になるはずです(sedはそれらを印刷しない場合でも、すべてを通過します-上記のようにq
uitオプションを指定しない限り) )。
例:
_sed -n '1{p;q}' /path/to/file
head -n 1 /path/to/file
_
繰り返しますが、私は効率をテストしませんでした。
大きなファイルから1行だけ(たとえば20行目)を印刷したい場合は、次のようにすることもできます。
head -20 filename | tail -1
私はbashで「基本的な」テストを行いましたが、上記のsed -n '1{p;q}
ソリューションよりもパフォーマンスが良いようです。
テストは大きなファイルを取り、中央のどこか(10000000
行)から行を出力し、次の行を選択するたびに100回繰り返します。そのため、行10000000,10000001,10000002, ...
が選択され、10000099
まで続きます。
$wc -l english
36374448 english
$time for i in {0..99}; do j=$((i+10000000)); sed -n $j'{p;q}' english >/dev/null; done;
real 1m27.207s
user 1m20.712s
sys 0m6.284s
vs.
$time for i in {0..99}; do j=$((i+10000000)); head -$j english | tail -1 >/dev/null; done;
real 1m3.796s
user 0m59.356s
sys 0m32.376s
複数のファイルから1行を印刷する場合
$wc -l english*
36374448 english
17797377 english.1024MB
3461885 english.200MB
57633710 total
$time for i in english*; do sed -n '10000000{p;q}' $i >/dev/null; done;
real 0m2.059s
user 0m1.904s
sys 0m0.144s
$time for i in english*; do head -10000000 $i | tail -1 >/dev/null; done;
real 0m1.535s
user 0m1.420s
sys 0m0.788s