Я пытаюсь подсчитать количество записей в 7,6 ГБ gzip-файле. Я нашел несколько подходов, используя zcat
команду.
$ zcat T.csv.gz | wc -l
423668947
Это работает, но это занимает слишком много времени (более 10 минут, чтобы получить счет). Я попробовал еще несколько подходов, таких как
$ sed -n '$=' T.csv.gz
28173811
$ perl -lne 'END { print $. }' < T.csv.gz
28173811
$ awk 'END {print NR}' T.csv.gz
28173811
Все три из этих команд выполняются довольно быстро, но дают неправильный счет 28173811.
Как я могу выполнить подсчет записей за минимальное количество времени?
Ответы:
В
sed
,perl
иawk
команды , которые вы упоминаете могут быть правильными, но они все прочитать сжатые данные и рассчитывает символы новой строки в этом. Эти символы новой строки не имеют ничего общего с символами новой строки в несжатых данных.Чтобы посчитать количество строк в несжатых данных, нет никакого способа их разархивировать. Ваш подход с
zcat
правильным подходом и поскольку данные настолько велики, что будет время , чтобы разархивировать его.Большинство утилит, которые имеют дело со
gzip
сжатием и распаковкой, скорее всего, будут использовать для этого одни и те же подпрограммы совместно используемых библиотек. Единственный способ ускорить его - найти реализациюzlib
подпрограмм, которые как-то быстрее, чем стандартные, и перестроить, например,zcat
для их использования.источник
zcat
. Значительная часть работыzcat
генерирует фактический результат. Но если вы считаете только\n
персонажей, это не обязательно.gzip
сжатие по существу работает, заменяя обычные длинные строки более короткими. Таким образом, вам нужно заботиться только о длинных строках в словаре, которые содержат\n
, и подсчитывать (взвешенные) их появления. Например, из-за английских правил,.\n
это обычная 16-битная строка.Используйте unpigz.
Ответ Kusalananda является правильным, вам будет нужно распаковывать , что весь файл для проверки его содержимого.
/bin/gunzip
делает это так быстро, как может, на одном ядре. Pigz - это параллельная реализация,gzip
которая может использовать несколько ядер.К сожалению, декомпрессия сам нормальных GZIP файлов не может быть распараллеливание, но
pigz
делает предложение улучшенную версиюgunzip
,unpigz
, что делает соответствующую работу , такие как чтение, запись и контрольную сумму в отдельном потоке. В некоторых быстрых тестахunpigz
это почти в два раза быстрее, чемgunzip
на моей базовой машине i5.Установите
pigz
с вашим любимым менеджером пакетов и используйтеunpigz
вместоgunzip
илиunpigz -c
вместоzcat
. Итак, ваша команда становится:Все это предполагает, что узким местом является процессор, а не диск, конечно.
источник
pigz
справочная страница утверждает, что распаковка не может быть распараллелена, по крайней мере, без специально подготовленных потоков дефляции для этой цели. В результате pigz использует один поток (основной поток) для распаковки, но создаст три других потока для чтения, записи и проверки вычислений, что может ускорить распаковку при некоторых обстоятельствах . Тем не менее, как и вы, я нахожу, что это как минимум вдвое быстрее, чемgzip
, если не из-за параллелизмаПроблема со всеми конвейерами в том, что вы по сути дела удваиваете работу. Независимо от того, насколько быстро выполняется декомпрессия, данные все равно необходимо перенести в другой процесс.
Perl имеет PerlIO :: gzip, который позволяет вам читать gzip-потоки напрямую. Следовательно, он может предложить преимущество, даже если его скорость распаковки может не соответствовать скорости
unpigz
:Я попытался сделать это с помощью сжатого файла gzip 13 МБ (распаковывается до 1,4 ГБ) на старом MacBook Pro 2010 года с 16 ГБ оперативной памяти и старом ThinkPad T400 с 8 ГБ оперативной памяти, где файл уже находится в кеше. На Mac скрипт Perl был значительно быстрее, чем при использовании конвейеров (5 секунд против 22 секунд), но в ArchLinux он проиграл unpigz:
против
и
Понятно, что использование здесь
unpigz -c file.gz | wc -l
является победителем в плане скорости. И эта простая командная строка наверняка превосходит написание программы, пусть даже короткой.источник
gzip | wc
такая же скорость, как у вашего сценария perl. Иpigz | wc
в два раза быстрее.gzip
работает с той же скоростью, независимо от того, записываю ли я вывод в / dev / null или pipe вwc
то, во что я верю, так это то, что «библиотека gzip», используемая perl, работает быстрее, чем инструмент командной строки gzip. Возможно, есть еще одна специфическая проблема Mac / Darwin с трубами. Удивительно, что эта версия на Perl вообще конкурентоспособна.zcat
и хуже, чемunpigz
. Я поражен тем, насколько быстрее конвейер в системе Linux по сравнению с Mac. Я не ожидал, что, хотя я должен был, как я однажды заметил, одна и та же программа работала быстрее на виртуальной Linux-машине с ограниченными процессорами на том же Mac, чем на голом железе.zcat | wc -l
и 5,5 с для вашего Perl-сценария. Честно говоря, я поражен тем, как люди здесь сообщают, особенно между Linux и MacOS X!wc -l
занимает 2,5 секунды.gzcat compressed.gz > /dev/null
занимает 2,7 секунды. Тем не менее, конвейер занимает 22 секунды. Если я попробую GNUwc
, для распакованного файла потребуется всего полсекунды, а в конвейере - 22 секунды. GNUzcat
выполняется вдвое дольшеzcat compressed.gz > /dev/null
. Это на Mavericks, старый процессор Core 2 Duo, 16 ГБ оперативной памяти, Crucial MX100 SSD.Ответ Кусалананды в основном правильный. Для подсчета строк вам нужно искать новые строки. Однако теоретически можно искать новые строки без полного распаковки файла.
gzip использует сжатие DEFLATE. DEFLATE представляет собой комбинацию кодировки LZ77 и Хаффмана. Может быть способ вычислить только символьный узел Хаффмана для новой строки и игнорировать все остальное. Почти наверняка есть способ искать строки, закодированные с использованием L277, вести подсчет байтов и игнорировать все остальное.
Так что ИМХО теоретически возможно придумать решение более эффективное, чем unpigz или zgrep. Это, как говорится, конечно, не практично (если кто-то уже не сделал это).
источник
Может быть сделано с помощью
zgrep
с-c
флагом, и$
параметром.В этом случае -c дает команду команде вывести количество совпавших строк, а регулярное выражение $ соответствует концу строки, чтобы оно соответствовало каждой строке или файлу.
Как прокомментировал @ StéphaneChazelas -
zgrep
это всего лишь сценарий вокругzcat
иgrep
и она должна обеспечивать одинаковую производительность с первоначальным предложениемzcat | wc -l
источник
zgrep
обычно это скрипт, который вызываетzcat
(так же, какgzip -dcq
) распаковывать данные и подавать ихgrep
, поэтому не поможет.Как видите, большинство ответов пытается оптимизировать то, что может: количество переключений контекста и межпроцессный ввод-вывод. Причина в том, что это единственное, что вы можете легко оптимизировать здесь.
Теперь проблема заключается в том, что потребность в ресурсах практически не зависит от потребности в декомпрессии. Вот почему оптимизация не сделает ничего быстрее.
Там, где это могло бы быть действительно ускорено, это был бы модифицированный алгоритм un-gzip (то есть декомпрессии), который исключал бы фактическое получение распакованного потока данных; скорее он только вычисляет количество новых строк в распакованном потоке из сжатого . Это было бы сложно, это потребовало бы глубокого знания алгоритма gzip (некоторая комбинация алгоритмов сжатия LZW и Хаффмана ). Вполне вероятно, что алгоритм не позволяет существенно оптимизировать время декомпрессии с молнией, нам нужно только знать количество строк новой строки. Даже если бы это было возможно, по сути, должна была быть разработана новая библиотека для распаковки gzip (она не существует, пока не узнают).
Реальный ответ на ваш вопрос таков: нет, вы не можете сделать это значительно быстрее.
Может быть, вы могли бы использовать распакованную распаковку gzip, если она существует. Он может использовать несколько процессорных ядер для распаковки. Если он не существует, он может быть относительно легко разработан.
Для xz существует параллельный компрессор (pxz).
источник