21

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

cat ${file} | head -1

また

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

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

4

5 に答える 5

38

の無駄な使用を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ボックスを使用しているため、タイミングは元の投稿とは異なります。

于 2013-03-26T08:50:40.483 に答える
6

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

もう1つの問題は、I/Oのタイミングパフォーマンス分析を行うことです。初めてファイルを開いてから読み取るとき、ファイルデータはおそらくメモリにキャッシュされていません。ただし、同じファイルで2番目のコマンドを再試行すると、データとiノードがキャッシュされているため、使用するコマンドに関係なく、タイミングの結果が速くなる可能性があります。さらに、iノードは実質的に永久にキャッシュされたままになります。たとえば、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

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

于 2013-03-26T12:49:16.613 に答える
3

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

例:

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

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

于 2013-03-26T10:13:49.110 に答える
3

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

対。

$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
于 2015-06-13T01:37:21.393 に答える
0

私は広範囲にわたるテストを行いましたが、ファイルのすべての行が必要な場合は、次のことがわかりました。

while IFS=$'\n' read LINE; do
  echo "$LINE"
done < your_input.txt

他の(Bashベースの)方法よりもはるかに高速です他のすべてのメソッド(などsed)は、少なくとも一致する行まで、毎回ファイルを読み取ります。ファイルの長さが4行の場合、次のようになります。1 -> 1,2 -> 1,2,3 -> 1,2,3,4=10読み取り、whileループは(に基づいてIFS)位置カーソルを維持するだけなので4、合計で読み取りのみを実行します。

〜15k行のファイルでは、違いは驚異的です。〜25〜28秒(sedベース、毎回特定の行を抽出)と〜0-1秒(while...readベース、ファイルを1回読み取る)

上記の例は、改行をより適切に設定する方法も示しています(以下のコメントのPeterに感謝します)。これにより、BashでIFS使用するときに見られる他の問題のいくつかが修正されることを願っています。while... read ...

于 2020-08-29T03:18:26.620 に答える