web-dev-qa-db-ja.com

ファイル内の1行を印刷する最速の方法

大きなファイルから特定の1行をフェッチする必要があります(1500000 lines)、複数のファイルをループして複数回、私は自分に何が最良のオプションかを尋ねていました(inパフォーマンスの条件)。これを行うには多くの方法があります。私はこれらを2つ使用します

cat ${file} | head -1

または

cat ${file} | sed -n '1p'

私はこれに対する答えを見つけることができませんでした。どちらも最初の行または2つのうちの1つだけをフェッチします(または両方)最初にファイル全体を開いてから、行1をフェッチしますか?

15
JBoy

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の時間はファイルの長さとともに直線的に増加しますが、他のバリエーションのタイミングは一定です(そして無視できます)最初の行を読んだ後、すべて終了したので:

enter image description here

注:より高速なLinuxボックス上にあるため、タイミングは元の投稿とは異なります。

29
Chris Seymour

本当に最初の行を取得して数百のファイルを読み取っているだけの場合は、外部の外部コマンドの代わりにシェルのビルトインを検討してください。bashとkshのシェルビルトインであるreadを使用してください。これにより、awksedheadなどによるプロセス作成のオーバーヘッドがなくなります。

もう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

これは明らかに不自然ですが、組み込みのパフォーマンスとコマンドの使用の違いを示しています。

5
jim mcnamara

パイプを避けるのはどうですか? sedheadはどちらも、引数としてファイル名をサポートしています。このようにして、猫のそばを通り過ぎることを避けます。私はそれを測定しませんでしたが、N行後に計算を停止するため、大きなファイルではヘッドが高速になるはずです(sedはそれらを印刷しない場合でも、すべてを通過します-上記のようにquitオプションを指定しない限り) )。

例:

_sed -n '1{p;q}' /path/to/file
head -n 1 /path/to/file
_

繰り返しますが、私は効率をテストしませんでした。

4

大きなファイルから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
2
dvvrt