Самый быстрый способ печати одной строки в файле
Мне нужно получить одну конкретную строку из большого файла (1500000 строк), несколько раз в цикле над несколькими файлами, я сам спрашивал себя, что будет лучшим вариантом (с точки зрения производительности).
Есть много способов сделать это, я мужественно использую эти 2
cat ${file} | head -1
или
cat ${file} | sed -n '1p'
Я не мог найти ответ на этот вопрос: они оба только берут первую строку или один из двух (или обоих) сначала открывают весь файл, а затем извлекают строку 1?
Ответы
Ответ 1
Оставьте бесполезное использование cat
и выполните:
$ sed -n '1{p;q}' file
После вывода строки будет выведено sed
script.
Бенчмаркинг script:
#!/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-панели.
Ответ 2
Если вы действительно просто получаете самую первую строку и читаете сотни файлов, то рассмотрите встроенные оболочки оболочки вместо внешних внешних команд, используйте read
, который является оболочкой, встроенной для bash и ksh. Это устраняет накладные расходы на создание процесса с помощью awk
, sed
, head
и т.д.
Другая проблема - это анализ времени выполнения ввода-вывода. При первом открытии и затем чтении файла данные файла, вероятно, не кэшируются в памяти. Однако, если вы снова попробуете вторую команду в том же файле, данные, а также inode будут кэшированы, поэтому результаты по времени могут быть быстрее, почти независимо от используемой вами команды. Кроме того, inodes могут оставаться кэшированными практически навсегда. Например, они относятся к Solaris. Или, в любом случае, несколько дней.
Например, linux кэширует все и кухонную раковину, что является хорошим атрибутом производительности. Но это делает проблематичным бенчмаркинг, если вы не знаете о проблеме.
Все эти эффекты кэширования влияют как на ОС, так и на аппаратные средства.
Итак - выберите один файл, прочитайте его командой. Теперь он кэшируется. Выполните ту же самую тестовую команду несколько десятков раз, это выборка эффекта создания команды и дочернего процесса, а не вашего оборудования ввода/вывода.
это sed vs read для 10 итераций получения первой строки одного и того же файла после прочтения файла один раз:
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
Это явно надуманно, но показывает разницу между встроенной производительностью и командой.
Ответ 3
Как избежать труб?
Оба sed
и head
поддерживают имя файла в качестве аргумента. Таким образом, вы избегаете прохождения мимо кошки. Я не измерял его, но голова должна быть быстрее на больших файлах, так как она останавливает вычисление после N строк (тогда как sed проходит через все из них, даже если он их не печатает), если вы не укажете опцию q
uit как было предложено выше).
Примеры:
sed -n '1{p;q}' /path/to/file
head -n 1 /path/to/file
Опять же, я не тестировал эффективность.
Ответ 4
Если вы хотите напечатать только одну строку (например, 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
Для печати строки из нескольких файлов
$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