web-dev-qa-db-ja.com

ファイルの最初の行を削除するのはどちらが速いですか... sedまたはtail?

この回答( sedでファイルの最初の行を削除するにはどうすればよいですか? )では、ファイルの最初のレコードを削除する2つの方法があります。

sed '1d' $file >> headerless.txt

** ---------------- OR ---------------- **

tail -n +2 $file >> headerless.txt

個人的には、tailオプションは見た目が美しく、読みやすいと思いますが、おそらく私はセドに挑戦しているからでしょう。

どの方法が最速ですか?

14

ファイルの最初の行を削除する場合のsedtailのパフォーマンス

TL; DR

  • sedは非常に強力で汎用性がありますが、特に多くの行を含む大きなファイルの場合、これが遅くなる原因です。

  • tailは単純なことを1つだけ実行しますが、その1つは、多くの行がある大きなファイルであっても、適切かつ高速に実行します。

中小規模のファイルの場合、sedおよびtailは同様に高速に実行されます(または、期待に応じて低速になります)。ただし、大きな入力ファイル(複数のMB)の場合、パフォーマンスの違いは大幅に大きくなり(数百MBの範囲のファイルでは1桁)、tailは明らかにsedを上回ります。

実験

一般的な準備:

分析するコマンドは次のとおりです。

sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null

パフォーマンスのボトルネックとしての端末出力またはファイル書き込みを排除するために、毎回/dev/nullに出力をパイプしていることに注意してください。

潜在的なボトルネックとしてのディスクI/Oを排除するために、RAMディスクをセットアップしましょう。個人的にtmpfs/tmpにマウントしているので、この実験のためにtestfileをそこに置いただけです。

次に、このコマンドを使用して、ランダムな行の長さとランダムなデータを含む指定量の行$numoflinesを含むランダムなテストファイルを作成します(それは明らかに最適ではないことに注意してください。気にしています、それは私たちが分析しているものではありません):

cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile

ああ、ところで。私のテスト用ラップトップは、Intel i5-6200U CPUでUbuntu 16.04、64ビットを実行しています。比較のためだけに。

大きなファイルのタイミング:

巨大なtestfileのセットアップ:

numoflines=10000000で上記のコマンドを実行すると、600 MBを少し超える10M行を含むランダムファイルが作成されました。

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

巨大なtestfileで時限実行を実行します。

では、まず両方のコマンドを使用して1回だけ実行し、どの程度の大きさで作業しているかを推定しましょう。

$ time sed '1d' testfile > /dev/null
real    0m2.104s
user    0m1.944s
sys     0m0.156s

$ time tail -n +2 testfile > /dev/null
real    0m0.181s
user    0m0.044s
sys     0m0.132s

大きなファイルの場合、すでに明確な結果が得られています。tailは、sedよりも高速です。ただし、楽しみのために、また大きな違いをもたらすランダムな副作用がないことを確認するために、100回行ってみましょう。

$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real    3m36.756s
user    3m19.756s
sys     0m15.792s

$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real    0m14.573s
user    0m1.876s
sys     0m12.420s

結論は変わりません。sedは大きなファイルの最初の行を削除するには非効率的であり、tailを使用する必要があります。

そして、はい、Bashのループ構造が遅いことは知っていますが、ここでは比較的少数の反復しか行っておらず、とにかくsed/tailランタイムに比べて単純なループにかかる時間は重要ではありません。

小さなファイルのタイミング:

小さなtestfileのセットアップ:

完全を期すために、kBの範囲に小さな入力ファイルがあるというより一般的なケースを見てみましょう。次のように、numoflines=100を使用してランダムな入力ファイルを作成しましょう。

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

$ head -n 3 testfile 
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly

小さなtestfileで時限実行を実行します。

このような小さなファイルのタイミングは経験から数ミリ秒の範囲にあると予想できるため、すぐに1000回の反復を実行してみましょう。

$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real    0m7.811s
user    0m0.412s
sys     0m7.020s

$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real    0m7.485s
user    0m0.292s
sys     0m6.020s

ご覧のとおり、タイミングは非常に似ており、解釈したり、疑問に思うことはあまりありません。小さいファイルの場合、両方のツールが同様に適しています。

28
Byte Commander

Bashビルトインとcatのみを使用する別の代替方法を次に示します。

{ read ; cat > headerless.txt; } < $file

$file{ }コマンドグループにリダイレクトされます。 readは、単に最初の行を読み取って破棄します。次に、ストリームの残りの部分はcatにパイプされ、宛先ファイルに書き込まれます。

Ubuntu 16.04では、これとtailソリューションのパフォーマンスは非常に似ています。 seqを使用して大きなテストファイルを作成しました。

$ seq 100000000 > 100M.txt
$ ls -l 100M.txt 
-rw-rw-r-- 1 ubuntu ubuntu 888888898 Dec 20 17:04 100M.txt
$

tailソリューション:

$ time tail -n +2 100M.txt > headerless.txt

real    0m1.469s
user    0m0.052s
sys 0m0.784s
$ 

cat/braceソリューション:

$ time { read ; cat > headerless.txt; } < 100M.txt 

real    0m1.877s
user    0m0.000s
sys 0m0.736s
$ 

しかし、今のところ便利なのはUbuntu VMだけで、両方とも同じ球場にいるにもかかわらず、両方のタイミングに大きな違いが見られます。

5
Digital Trauma

私のシステムを試して、各コマンドの前にtimeを付けると、次の結果が得られました。

sed:

real    0m0.129s
user    0m0.012s
sys     0m0.000s

と尾:

real    0m0.003s
user    0m0.000s
sys     0m0.000s

これは、Ubuntu 16.04を実行している少なくともAMD FX 8250のシステムでは、tailが大幅に高速であることを示唆しています。テストファイルには、540kのサイズで10,000行ありました。ファイルはHDDから読み取られました。

4
Nick Sillito

プログラムの実行中にシステムで実行されるのはsedtailだけではないため、どちらが優れているかを客観的に示す方法はありません。ディスクI/O、ネットワークI/O、優先度の高いプロセスのCPU割り込みなど、多くの要素がすべて、プログラムの実行速度に影響します。

どちらもCで書かれているため、これは言語の問題ではなく、環境の問題です。たとえば、SSDを使用しているため、システム上ではマイクロ秒単位で時間がかかりますが、HDDが大幅に遅いため、ハードドライブ上の同じファイルの場合は時間がかかります。したがって、これでもハードウェアが役割を果たします。

どのコマンドを選択するかを検討する際に留意すべきことがいくつかあります。

  • 君の目的は何 ? sedは、テキストを変換するためのストリームエディターです。 tailは、特定のテキスト行を出力するためのものです。行を処理してそれらを出力するだけの場合は、tailを使用します。テキストを編集する場合は、sedを使用します。
  • tailsedよりもはるかに単純な構文なので、自分で読めるものと他の人が読めるものを使用してください。

もう1つの重要な要素は、処理しているデータの量です。小さなファイルを使用しても、パフォーマンスに違いはありません。大きなファイルを処理している場合、この画像は興味深いものになります。 2 GBのBIGFILE.txtを使用すると、sedにはtailよりもはるかに多くのシステムコールがあり、実行速度がかなり遅いことがわかります。

bash-4.3$ du -sh BIGFILE.txt 
2.0G    BIGFILE.txt
bash-4.3$ strace -c  sed '1d' ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 59.38    0.079781           0    517051           read
 40.62    0.054570           0    517042           write
  0.00    0.000000           0        10         1 open
  0.00    0.000000           0        11           close
  0.00    0.000000           0        10           fstat
  0.00    0.000000           0        19           mmap
  0.00    0.000000           0        12           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         7         7 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         2         2 statfs
  0.00    0.000000           0         1           Arch_prctl
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.134351               1034177        11 total
bash-4.3$ strace -c  tail  -n +2 ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 62.30    0.148821           0    517042           write
 37.70    0.090044           0    258525           read
  0.00    0.000000           0         9         3 open
  0.00    0.000000           0         8           close
  0.00    0.000000           0         7           fstat
  0.00    0.000000           0        10           mmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         3         3 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           Arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.238865                775615         7 total
1

一番上の答えは、> /dev/nullを行う際にディスクを考慮していませんでした

大きなファイルがあり、ディスク上に一時的な複製を作成したくない場合は、vim -cを試してください

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time sed -i '1d' testfile

real    0m59.053s
user    0m9.625s
sys     0m48.952s

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time vim -e -s testfile -c ':1d' -c ':wq'

real    0m8.259s
user    0m3.640s
sys     0m3.093s

編集:ファイルが利用可能なメモリよりも大きい場合vim -cが機能しない場合、ファイルの増分ロードを行うには十分に賢くないように見えます

1
StevenWernerCS

他の答えは、最初の行が欠落している新しいファイルを作成する方が良いことをよく示しています。ただし、新しいファイルを作成するのではなく、ファイルを編集する場合は、edのほうが新しいファイルを作成する必要がないため、より高速になるはずです。しかし、edを使用して行を削除する方法を検索する必要があります。これは、一度だけ使用したためです。

0
akostadinov