Самый быстрый способ печати одной строки в файле

Мне нужно получить одну конкретную строку из большого файла (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