Что быстрее удалить первую строку в файле... sed или tail?
В этом ответе ( Как я могу удалить первую строку файла с помощью sed?) Есть два способа удалить первую запись в файле:
sed '1d' $file >> headerless.txt
** ---------------- ИЛИ ЖЕ ----------------**
tail -n +2 $file >> headerless.txt
Лично я думаю, что tail
вариант косметически более приятен и более читабелен, но, вероятно, потому, что я бросил ему вызов.
Какой метод самый быстрый?
6 ответов
Производительность sed
против tail
убрать первую строку файла
TL;DR
sed
очень мощный и универсальный, но это делает его медленным, особенно для больших файлов с большим количеством строк.tail
делает только одну простую вещь, но она делает это хорошо и быстро, даже для больших файлов с множеством строк.
Для небольших и средних файлов, sed
а также tail
выполняют так же быстро (или медленно, в зависимости от ваших ожиданий). Однако для больших входных файлов (несколько МБ) разница в производительности значительно возрастает (на порядок для файлов в диапазоне сотен МБ), причем tail
явно превосходит sed
,
эксперимент
Общие препараты:
Наши команды для анализа:
sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null
Обратите внимание, что я передаю вывод /dev/null
каждый раз для устранения вывода терминала или записи файла в качестве узкого места производительности.
Давайте настроим RAM-диск для устранения дискового ввода-вывода как потенциального узкого места. У меня лично есть tmpfs
установлен в /tmp
так что я просто разместил свой testfile
там для этого эксперимента.
Затем я однажды создаю случайный тестовый файл, содержащий указанное количество строк $numoflines
со случайной длиной строки и случайными данными, использующими эту команду (обратите внимание, что она определенно не оптимальна, она становится действительно медленной для>2M строк, но кого это волнует, это не то, что мы анализируем):
cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile
О, кстати. мой тестовый ноутбук работает под управлением Ubuntu 16.04, 64-разрядная на процессоре Intel i5-6200U. Просто для сравнения.
Сроки больших файлов:
Настройка огромного testfile
:
Выполнение команды выше с numoflines=10000000
произвел случайный файл, содержащий 10M строк, занимающих чуть более 600 МБ - он довольно большой, но давайте начнем с него, потому что мы можем:
$ wc -l testfile
10000000 testfile
$ du -h testfile
611M testfile
$ head -n 3 testfile
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO
Выполните запуски по времени с нашим огромным testfile
:
Теперь давайте сначала выполним однократный запуск обеих команд, чтобы оценить, с какими величинами мы работаем.
$ 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
:
Теперь для полноты давайте рассмотрим более распространенный случай, когда у вас есть небольшой входной файл в диапазоне КБ. Давайте создадим случайный входной файл с 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
Как видите, сроки очень похожи, толковать или удивлять особо нечего. Для небольших файлов оба инструмента одинаково хорошо подходят.
Вот еще один вариант, используя только встроенные команды 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
/ скобное решение:
$ time { read ; cat > headerless.txt; } < 100M.txt
real 0m1.877s
user 0m0.000s
sys 0m0.736s
$
Хотя сейчас у меня есть только Ubuntu VM, и я видел значительные различия во времени обоих, хотя они все находятся на одном уровне.
Попытка в моей системе и префикс каждой команды с time
Я получил следующие результаты:
СЭД:
real 0m0.129s
user 0m0.012s
sys 0m0.000s
и хвост:
real 0m0.003s
user 0m0.000s
sys 0m0.000s
что говорит о том, что на моей системе хотя бы AMD FX 8250 с Ubuntu 16.04 хвост значительно быстрее. Тестовый файл имел 10 000 строк размером 540 КБ. Файл был прочитан с жесткого диска.
Нет объективного способа сказать, что лучше, потому что sed
а также tail
не единственные вещи, которые запускаются в системе во время выполнения программы. Множество факторов, таких как дисковый ввод-вывод, сетевой ввод-вывод, прерывания процессора для процессов с более высоким приоритетом - все это влияет на скорость выполнения вашей программы.
Они оба написаны на C, так что это не языковая проблема, а скорее экологическая. Например, у меня есть SSD, и в моей системе это займет время в микросекундах, но для того же файла на жестком диске это займет больше времени, потому что жесткие диски значительно медленнее. Таким образом, аппаратная часть играет роль в этом тоже.
Есть несколько вещей, которые вы можете иметь в виду при выборе команды:
- Какова ваша цель?
sed
потоковый редактор для преобразования текста.tail
для вывода определенных строк текста. Если вы хотите разобраться со строками и только распечатать их, используйтеtail
, Если вы хотите редактировать текст, используйтеsed
, tail
имеет гораздо более простой синтаксис, чемsed
так что используйте то, что вы можете прочитать сами, а что могут читать другие.
Другим важным фактором является объем данных, которые вы обрабатываете. Небольшие файлы не дадут вам никакой разницы в производительности. Картина становится интересной, когда вы имеете дело с большими файлами. С 2 ГБ 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
Топ ответ не принимал во внимание диск > /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
не работает, похоже, что он не достаточно умен, чтобы сделать дополнительную загрузку файла
Другие ответы хорошо показывают, что лучше создать новый файл с отсутствующей первой строкой. Если вы хотите отредактировать файл, а не создавать новый файл, я готов поспорить, ed
будет быстрее, потому что это не должно создавать новый файл вообще. Но вы должны искать, как удалить строку с ed
потому что я использовал это только один раз.