Продуктивність sed
vs. 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
із випадковою довжиною рядка та випадковими даними, використовуючи цю команду (зауважте, що це точно не оптимально, він стає дійсно повільним для приблизно> 2М рядків, але кого це цікавить, це не що ми аналізуємо):
cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile
Ой, дощо. мій тестовий ноутбук працює на Ubuntu 16.04, 64 біт на процесорі Intel i5-6200U. Просто для порівняння.
Час великих файлів:
Налаштування величезного testfile
:
Запустити команду вище із numoflines=10000000
створеним випадковим файлом, що містить 10М рядків, займає трохи більше 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
слід використовувати там.
І так, я знаю, що конструктивні петлі Баша повільні, але ми тут робимо лише мало ітерацій, і час, який звичайний цикл займає, не є істотним порівняно з 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
Як бачите, таймінги досить схожі, тлумачити чи дивуватися не так вже й багато. Для невеликих файлів обидва інструменти однаково добре підходять.
sed
це портативніше: "+2" дляtail
відмінної роботи на Ubuntu, який використовує GNUtail
, але не працює на BSDtail
.