Подтвердить что ты не робот

Подсчитывать строки в больших файлах

Я обычно работаю с текстовыми файлами размером ~ 20 Гб и очень часто считаю количество строк в заданном файле.

То, как я делаю это сейчас, просто cat fname | wc -l, и это занимает очень много времени. Есть ли какое-нибудь решение, которое будет намного быстрее?

Я работаю в высокопроизводительном кластере с установленным Hadoop. Мне было интересно, поможет ли подход с уменьшением карты.

Я хотел бы, чтобы решение было таким же простым, как и однострочное, например, решение wc -l, но не уверен, насколько это возможно.

Любые идеи?

4b9b3361

Ответ 1

Попробуйте: sed -n '$=' filename

Также кошка не нужна: wc -l filename достаточно в вашем нынешнем виде.

Ответ 2

Ваш предельный коэффициент скорости - это скорость ввода-вывода вашего устройства хранения, поэтому изменение между простыми программами подсчета строк/шаблонов не поможет, так как разница в скорости выполнения между этими программами, вероятно, будет подавлена ​​тем, что медленный диск/хранилище/все, что у вас есть.

Но если у вас есть тот же файл, который скопирован на диски/устройства, или файл распространяется среди этих дисков, вы можете выполнить операцию параллельно. Я не знаю конкретно об этом Hadoop, но, предполагая, что вы можете прочитать файл 10gb из 4 разных местоположений, вы можете запускать 4 разных процесса подсчета строк, каждый из которых находится в одной части файла, и суммировать их результаты:

$ dd bs=4k count=655360 if=/path/to/copy/on/disk/1/file | wc -l &
$ dd bs=4k skip=655360 count=655360 if=/path/to/copy/on/disk/2/file | wc -l &
$ dd bs=4k skip=1310720 count=655360 if=/path/to/copy/on/disk/3/file | wc -l &
$ dd bs=4k skip=1966080 if=/path/to/copy/on/disk/4/file | wc -l &

Обратите внимание на & в каждой командной строке, поэтому все будет выполняться параллельно; dd здесь работает как cat, но позволяет указать, сколько байтов читать (count * bs bytes) и сколько пропустить в начале ввода (skip * bs bytes). Он работает в блоках, следовательно, необходимо указать bs как размер блока. В этом примере я разбил файл 10Gb на 4 равных фрагмента 4Kb * 655360 = 2684354560 байт = 2,5 ГБ, по одному для каждого задания, вы можете настроить script, чтобы сделать это для вас на основе размера файла и количество выполняемых вами параллельных заданий. Вам также нужно суммировать результат выполнения, чего я не сделал для моей нехватки возможностей оболочки script.

Если ваша файловая система достаточно умна, чтобы разделить большой файл среди многих устройств, например RAID или распределенной файловой системы или что-то еще, и автоматически распараллеливать запросы ввода-вывода, которые могут быть паралеллированы, вы можете сделать такой раскол, выполняя множество параллельных заданий, но используя тот же путь к файлу, и у вас все еще может быть некоторое увеличение скорости.

EDIT: Другая идея, которая возникла для меня, состоит в том, что если строки внутри файла имеют одинаковый размер, вы можете получить точное количество строк, разделив размер файла на размер строки, как в байтах. Вы можете сделать это почти мгновенно в одной работе. Если вы имеете средний размер и не заботитесь точно о количестве строк, но хотите получить оценку, вы можете сделать эту же операцию и получить удовлетворительный результат намного быстрее, чем точная операция.

Ответ 3

На многоядерном сервере используйте параллельную параллельную параллельную параллельную параллельную параллельную параллельную параллельную параллельную линию строк. После того, как будет напечатано количество строк в строке, bc суммирует все подсчеты строк.

find . -name '*.txt' | parallel 'wc -l {}' 2>/dev/null | paste -sd+ - | bc

Чтобы сэкономить место, вы можете даже сжать все файлы. Следующая строка распаковывает каждый файл и подсчитывает его строки параллельно, а затем суммирует все значения.

find . -name '*.xz' | parallel 'xzcat {} | wc -l' 2>/dev/null | paste -sd+ - | bc

Ответ 4

Если ваши данные хранятся на HDFS, возможно, самый быстрый подход - использовать потоки хаопов. Apache Pig COUNT UDF работает на сумке и поэтому использует один редуктор для вычисления количества строк. Вместо этого вы можете вручную установить количество редукторов в простой потоке хаоса script следующим образом:

$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar -Dmapred.reduce.tasks=100 -input <input_path> -output <output_path> -mapper /bin/cat -reducer "wc -l"

Обратите внимание, что я вручную устанавливаю количество редукторов до 100, но вы можете настроить этот параметр. Как только работа по сокращению карты выполняется, результат каждого редуктора сохраняется в отдельном файле. Конечное количество строк - это сумма чисел, возвращаемых всеми редукторами. вы можете получить окончательное количество строк следующим образом:

$HADOOP_HOME/bin/hadoop fs -cat <output_path>/* | paste -sd+ | bc

Ответ 5

В соответствии с моим тестом я могу проверить, что Spark-Shell (на основе Scala) работает быстрее, чем другие инструменты (GREP, SED, AWK, PERL, WC). Вот результат теста, который я запускал в файле, который имел 23782409 строк

time grep -c $ my_file.txt;

real 0m44.96s пользователь 0m41.59s sys 0m3.09s

time wc -l my_file.txt;

real 0m37.57s пользователь 0m33.48s sys 0m3.97s

time sed -n '$=' my_file.txt;

real 0m38.22s пользователь 0m28.05s sys 0m10.14s

time perl -ne 'END { $_=$.;if(!/^[0-9]+$/){$_=0;};print "$_" }' my_file.txt;

real 0m23.38s пользователь 0m20.19s sys 0m3.11s

time awk 'END { print NR }' my_file.txt;

real 0m19.90s пользователь 0m16.76s sys 0m3.12s

spark-shell
import org.joda.time._
val t_start = DateTime.now()
sc.textFile("file://my_file.txt").count()
val t_end = DateTime.now()
new Period(t_start, t_end).toStandardSeconds()

res1: org.joda.time.Seconds = PT15S

Ответ 6

Hadoop по существу обеспечивает механизм для выполнения чего-то подобного тому, что предлагает @Ivella.

Hadoop HDFS (распределенная файловая система) собирается взять ваш 20-гигабайтный файл и сохранить его по кластеру в блоках фиксированного размера. Допустим, вы настроили размер блока 128 МБ, файл будет разбит на блоки размером 20х8х128 МБ.

Затем вы запустили программу сокращения карты по этим данным, по существу подсчитывая строки для каждого блока (на этапе карты), а затем уменьшая количество строк блока до окончательного количества строк для всего файла.

Что касается производительности, то в целом чем больше ваш кластер, тем лучше производительность (больше wc работает параллельно, более независимых дисков), но есть некоторые накладные расходы при работе в оркестровке, что означает, что выполнение задания на небольших файлах не будет фактически обеспечивают более быструю пропускную способность, чем запуск локального wc

Ответ 7

Я знаю, что вопрос уже несколько лет, но, расширяя последнюю идею Ivella, этот скрипт bash оценивает количество строк большого файла за считанные секунды или меньше, измеряя размер одной строки и экстраполируя ее:

#!/bin/bash
head -2 $1 | tail -1 > $1_oneline
filesize=$(du -b $1 | cut -f -1)
linesize=$(du -b $1_oneline | cut -f -1)
rm $1_oneline
echo $(expr $filesize / $linesize)

Если вы lines.sh этот скрипт lines.sh, вы можете вызвать lines.sh bigfile.txt чтобы получить оценочное количество строк. В моем случае (около 6 ГБ, экспорт из базы данных) отклонение от истинного количества строк составляло всего 3%, но прошло около 1000 раз быстрее. Кстати, я использовал вторую, а не первую строчку в качестве основы, потому что первая строка имела имена столбцов, а фактические данные начинались во второй строке.

Ответ 8

Я не уверен, что python быстрее:

[[email protected] scripts]# time python -c "print len(open('mybigfile.txt').read().split('\n'))"

644306


real    0m0.310s
user    0m0.176s
sys     0m0.132s

[[email protected] scripts]# time  cat mybigfile.txt  | wc -l

644305


real    0m0.048s
user    0m0.017s
sys     0m0.074s

Ответ 9

Если ваше узкое место - это диск, важно, как вы его читаете. dd if=filename bs=128M | wc -l намного быстрее, чем wc -l filename или cat filename | wc -l для моей машины с жестким диском и быстрым процессором и оперативной памятью. Вы можете играть с размером блока и видеть, что dd сообщает как пропускную способность. Я свернул его до 1GiB.

Примечание. Существует некоторая дискуссия о том, быстрее ли cat или dd. Все, что я утверждаю, заключается в том, что dd может быть быстрее, в зависимости от системы, и что это для меня. Попробуйте сами.

Ответ 10

Если ваш компьютер имеет python, вы можете попробовать это из оболочки:

python -c "print len(open('test.txt').read().split('\n'))"

Это использует python -c, чтобы передать команду, которая в основном читает файл, и разделяет "новая строка", чтобы получить количество строк новой строки или общую длину файла.

@BlueMoon:

bash-3.2$ sed -n '$=' test.txt
519

Используя вышеизложенное:

bash-3.2$ python -c "print len(open('test.txt').read().split('\n'))"
519

Ответ 11

find  -type f -name  "filepattern_2015_07_*.txt" -exec ls -1 {} \; | cat | awk '//{ print $0 , system("cat " $0 "|" "wc -l")}'

Выход:

Ответ 12

Предположим:

  • Ваша файловая система распределена
  • Ваша файловая система может легко заполнить сетевое подключение на один узел
  • Вы получаете доступ к своим файлам, как обычные файлы

то вы действительно хотите нарезать файлы на части, подсчитывать части параллельно на нескольких узлах и суммировать результаты оттуда (это в основном идея @Chris White).

Вот как вы это делаете с GNU Parallel (версия> 20161222). Вам нужно перечислить узлы в ~/.parallel/my_cluster_hosts и вы должны иметь доступ к ssh всем из них:

parwc() {
    # Usage:
    #   parwc -l file                                                                

    # Give one chunck per host                                                     
    chunks=$(cat ~/.parallel/my_cluster_hosts|wc -l)
    # Build commands that take a chunk each and do 'wc' on that                    
    # ("map")                                                                      
    parallel -j $chunks --block -1 --pipepart -a "$2" -vv --dryrun wc "$1" |
        # For each command                                                         
        #   log into a cluster host                                                
        #   cd to current working dir                                              
        #   execute the command                                                    
        parallel -j0 --slf my_cluster_hosts --wd . |
        # Sum up the number of lines                                               
        # ("reduce")                                                               
        perl -ne '$sum += $_; END { print $sum,"\n" }'
}

Использовать как:

parwc -l myfile
parwc -w myfile
parwc -c myfile