Есть ли способ сделать этот однострочный лайнер быстрее?

контекст

У меня есть каталог из тысяч zip-файлов, датированных в форме YYYYMMDD_hhmmss.zip и каждый около 300K. Внутри каждого zip-файла около 400 xml-файлов каждый около 3K.

Проблема

Мне нужно иметь возможность искать и находить заданную строку в диапазоне дат zip-файлов.

Текущее (хотя и посредственное) решение

У меня есть следующий однострочный

 find /home/mydir/ -type f | sort | \ awk "/xml_20140207_000016.zip/,/xml_20140207_235938.zip/" | \ xargs -n 1 -P 10 zipgrep "my search string" 

Дело в том, что

  1. перечислить все файлы в тысячах файлов
  2. сортировать этот список файлов
  3. извлекать ряд файлов на основе заданных дат (эта команда awk выводит строки только после первой строки с совпадением и до этой второй строки)
  4. передать каждую строку результата, которая соответствует одному файлу zipgrep

Вопрос

Этот однострочный динамик работает очень медленно, даже с 10 процессами на 24-ядерной машине. Я считаю, что это медленное из-за команды zipgrep но я недостаточно zipgrep , чтобы знать, как его улучшить. Я не знаю, должен ли я быть, но я немного смущен тем, что коллега написал Java-инструмент, который работает быстрее, чем этот скрипт. Я бы хотел, если это возможно, отменить это. Тогда кто-нибудь знает, как сделать эту команду быстрее в этом контексте? Или улучшить какую-либо его часть?

  • Найдите полный путь и имя файла под каталогом, затем перейдите в исполняемый файл в качестве аргументов
  • передавая переменные от 'ls' до 'tar' через 'xargs'
  • cmd2 `cmd1` vs cmd1 | xargs cmd2
  • Как я могу объединить все файлы в каталоге вместе в одной операции с командной строкой?
  • Прикрепление файла в Unix для данной строки, а затем другой набор строк в этом файле и печать свойств файла тоже
  • xargs не работает в Linux, поскольку работает в Unix
  • Используйте команду find для выполнения ряда команд
  • xargs внутри цикла for
  • 3 Solutions collect form web for “Есть ли способ сделать этот однострочный лайнер быстрее?”

    Есть часть, которую вы можете легко улучшить, но это не самая медленная часть.

     find /home/mydir/ -type f | sort | \ awk "/xml_20140207_000016.zip/,/xml_20140207_235938.zip/" 

    Это несколько расточительно, потому что он сначала перечисляет все файлы, затем сортирует имена файлов и извлекает интересные. Команда find должна завершиться до начала сортировки.

    Было бы быстрее перечислять только интересные файлы в первую очередь или, по крайней мере, как можно более мелкие надмножества. Если вам нужен фильтр с более мелким зерном по именам, чем find способен, подключитесь к awk, но не сортируйте: awk и другие по очереди фильтры могут обрабатывать строки один за другим, но сортировка требует полного ввода.

     find /home/mydir/ -name 'xml_20140207_??????.zip' -type f | \ awk 'match($0, /_[0-9]*.zip$/) && (time = substr($0, RSTART+1, RLENGTH-5)) && time >= 16 && time <= 235938' | xargs -n 1 -P 10 zipgrep "my search string" 

    Частью, которая, очевидно, является субоптимальной, является zipgrep. Здесь нет простого способа повысить производительность из-за ограничений программирования оболочки. Сценарий zipgrep работает, перечисляя имена файлов в архиве и вызывая grep для содержимого каждого файла один за другим. Это означает, что zip-архив обрабатывается снова и снова для каждого файла. Программа Java (или Perl, или Python, или Ruby и т. Д.) Может избежать этого, обрабатывая файл только один раз.

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

     … | xargs -n1 -P2 sh -c ' mkdir "mnt$$-$1"; fuse-zip "$1" "mnt$$-$1"; grep -R "$0" "mnt$$-$1" fusermount -u "mnt$$-$1" ' "my search string" 

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

    Я ничего не тестировал, но я думаю, что самым большим местом для улучшения было бы использование реализации zipgrep на более мощном языке.

    Некоторые быстрые идеи;

    • Если все файлы находятся в одном каталоге, вы можете избавиться от find
    • Соглашение об имени вашего файла сортируется по дате, поэтому вам не нужен бит sort
    • С этими двумя частями в стороне, и если диапазон дат известен, вы можете использовать простой глобус имени файла вместо awk. Например (если ваша оболочка bash ):

      • Все файлы за один день

        echo xml_20140207_*.zip | xargs -n 1 -P 10 zipgrep "my search string"

      • Файлы, созданные с 15:00 до 18:00, либо с 07 февраля, либо 10 февраля 2014 года:

        echo xml_201402{07,10}_1{5..7}*.zip | xargs -n 1 -P 10 zipgrep "my search string"

    Неясно, где ваше узкое место. Предположим, что это при чтении файлов. В зависимости от вашей системы хранения, перед ее обработкой быстрее читать весь файл. Это особенно верно для zipgrep который делает несколько zipgrep в файл: если файл не полностью в памяти, вы будете ждать, пока диск будет искать.

     find ... | parallel -j1 'cat {} >/dev/null; echo {}' | parallel zipgrep "my search string" 

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

    Я использовал RAID-системы, где вы получили 6-кратное ускорение за счет чтения 10 файлов параллельно, чем чтение 1 файла за раз или одновременное чтение 30 файлов. Если бы мне пришлось запустить выше в этой RAID-системе, я бы -j1 в -j10 .

    Используя GNU Parallel вместо xargs вы xargs себя от смешивания вывода (см. http://www.gnu.org/software/parallel/man.html#DIFFERENCES-BETWEEN-xargs-AND-GNU-Parallel ).

    Linux и Unix - лучшая ОС в мире.