Производительность цикла против расширения

9

Нужны экспертные предложения по сравнению ниже:

Сегмент кода с использованием цикла:

for file in `cat large_file_list`
do
    gzip -d $file
done

Сегмент кода с использованием простого расширения:

gzip -d `cat large_file_list`

Какой из них будет быстрее? Приходится манипулировать большим набором данных.

Леон
источник
1
Правильный ответ будет зависеть от того, сколько времени потребуется для запуска gzipв вашей системе, количества файлов в списке файлов и размера этих файлов.
Кусалананда
Список файлов будет иметь около 1000 - 10000 файлов. Размер варьируется от нескольких килобайт до 500 МБ. Я понятия не имею, сколько времени потребуется, чтобы запустить gzip в моей системе. Любой способ проверить?
Леон
1
Хорошо, тогда это также может зависеть от длины имен файлов . Если имена файлов длинные, некоторые системы могут генерировать ошибку «список аргументов слишком длинный», если вы попытаетесь сделать это без цикла, поскольку подстановка команды приведет к слишком длинной командной строке для выполнения оболочки. Если вы не хотите зависеть от количества файлов в списке, просто используйте цикл. Вы тратите значительное количество времени на распаковку этих файлов по сравнению с другой обработкой, которую вы будете выполнять с ними?
Кусалананда
Леон, посмотрите на мои результаты тестов: «огромный арглист» в 20 раз быстрее, чем «цикл» в моих настройках.
для счастливого промежуточного значения между xargs gzip -d < large_file_listtr \\n \\0 large_file_list | xargs -0 gzip -d
запуском

Ответы:

19

осложнения

Следующее будет работать только иногда:

gzip -d `cat large_file_list`

Три проблемы (в bashбольшинстве других оболочек типа Борна):

  1. Он потерпит неудачу, если в любом имени файла есть пробел или символы новой строки (при условии, что $IFSон не был изменен). Это из-за разбиения слов в оболочке .

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

  3. Также произойдет сбой, если имена файлов начинаются с -(если POSIXLY_CORRECT=1это относится только к первому файлу) или если какое-либо имя файла есть -.

  4. Также произойдет сбой, если в нем слишком много имен файлов, чтобы поместиться в одной командной строке.

Код ниже подвержен тем же проблемам, что и код выше (кроме четвертого)

for file in `cat large_file_list`
do
    gzip -d $file
done

Надежное решение

Если у вас large_file_listесть ровно одно имя файла в строке, и названный файл -не входит в их число, и вы работаете в системе GNU, тогда используйте:

xargs -rd'\n' gzip -d -- <large_file_list

-d'\n'говорит, что xargsнужно обрабатывать каждую строку ввода как отдельное имя файла.

-rговорит xargsне запускать команду, если входной файл пуст.

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

xargsпоместит много имен файлов в каждой командной строке, но не так много, чтобы оно превысило предел командной строки. Это уменьшает количество раз, которое gzipпроцесс должен быть запущен и, следовательно, делает это быстро. Это также безопасно: имена файлов также будут защищены от разделения слов и расширения пути .

John1024
источник
Спасибо за подробный ответ. Я понимаю ваши упомянутые 3 вопроса. Имя файла простое и не столкнется с этими проблемами, так как список будет содержать до 20000. И мой вопрос в основном о производительности этих двух сегментов. Спасибо.
Леон
1
@Leon forЦикл будет, безусловно, самым медленным. Два других метода будут очень близки по скорости друг к другу.
John1024
7
Кроме того, не стоит сбрасывать со счетов потенциальные проблемы: многие вопросы здесь, на StackExchange, связаны с тем, что разделение слов или расширение пути произошло с людьми, которые этого не ожидали.
John1024
5
Также обратите внимание, что есть вариация при чтении файла с xargs: по крайней мере, в версии GNU есть --arg-fileопция (краткая форма -a). Так что можно сделать xargs -a large_file_list -rd'\n' gzip -d вместо этого. Фактически, нет никакой разницы, кроме того факта, что он <является оператором оболочки и будет xargsвыполнять чтение из stdin (который оболочка «связывает» с файлом), в то время как он -aбудет xargsявно открывать рассматриваемый файл
Сергей Колодяжный
2
Terdon отметил в другом комментарии об использовании parallelдля запуска нескольких копий gzip, но xargs(по крайней мере, один GNU), также имеет -Pпереключатель для этого. На многоядерных машинах это может иметь значение. Но также возможно, что декомпрессия полностью связана с вводом / выводом.
ilkkachu
12

Я сомневаюсь, что это будет иметь большое значение.

Я бы использовал цикл только потому, что я не знаю, сколько файлов указано в списке файлов, и я не знаю (обычно), есть ли в именах файлов пробелы. Выполнение подстановки команды, которая генерирует очень длинный список аргументов, может привести к ошибке «Список аргументов слишком длинный», когда длина генерируемого списка слишком велика.

Моя петля будет выглядеть так

while IFS= read -r name; do
    gunzip "$name"
done <file.list

Это дополнительно позволило бы мне вставить команды для обработки данных после gunzipкоманды. Фактически, в зависимости от того, что на самом деле представляют собой данные и что нужно с ними делать, может быть даже возможно обработать их, не сохраняя их вообще в файл:

while IFS= read -r name; do
    zcat "$name" | process_data
done <file.list

(где process_dataкакой-то конвейер, который читает несжатые данные из стандартного ввода)

Если обработка данных занимает больше времени, чем распаковка, вопрос о том, является ли цикл более эффективным или нет, становится неактуальным.

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

for name in ./*.gz; do
    # processing of "$name" here
done

где ./*.gzобразец, соответствующий соответствующим файлам. Таким образом, мы не зависим от количества файлов или символов, используемых в именах файлов (они могут содержать символы новой строки или другие пробельные символы, или начинаться с тире и т. Д.)

Связанные с:

Кусалананда
источник
5

Из этих двух тот, у которого все файлы передаются за один вызов, gzipскорее всего будет быстрее, потому что вам нужно запустить gzipтолько один раз. (То есть, если команда работает вообще, см. Другие ответы для предостережений.)

Но я хотел бы напомнить о золотом правиле оптимизации : не делайте это преждевременно.

  1. Не оптимизируйте подобные вещи, пока не узнаете, что это проблема.

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

  2. Мера. На самом деле, это лучший способ быть уверенным.

    Вы увидите результаты своими глазами (или с вашим собственным секундомером), и они будут применяться к вашей ситуации, чего не могут быть случайные ответы в Интернете. Поместите оба варианта в скрипты и запустите time script1.sh, и time script2.sh. (Сделайте это со списком пустых сжатых файлов, чтобы измерить абсолютную величину накладных расходов.)

ilkkachu
источник
0

Как быстро ваш диск?

Это должно использовать все ваши процессоры:

parallel -X gzip -d :::: large_file_list

Таким образом, ваш предел, вероятно, будет скорость вашего диска.

Вы можете попробовать настроить с помощью -j:

parallel -j50% -X gzip -d :::: large_file_list

Это будет запускать половину заданий параллельно с предыдущей командой и меньше нагружать ваш диск, поэтому в зависимости от вашего диска это может быть быстрее.

Оле Танге
источник