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


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
Гаразд, тоді це також може залежати від довжини імен файлів . Якщо назви файлів довгі, деякі системи можуть генерувати помилку "список аргументів занадто довгий", якщо ви намагаєтеся зробити це без циклу, оскільки заміна команди призведе до занадто довгого командного рядка для оболонки, який повинен виконуватись. Якщо ви не хочете залежати від кількості файлів у списку, просто використовуйте цикл. Ви витрачаєте значну кількість часу на декомпресію цих файлів порівняно з іншими обробками, які ви будете виконувати над ними?
Kusalananda

Леон поглянь на мої результати тесту: "величезний аргуст" в 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процес повинен бути запущений, і тому робить це швидким. Це також безпечно: імена файлів також будуть захищені від розбиття слів та розширення імені шляху .


Дякуємо за детальну відповідь. Я розумію ваші згадані 3 питання. Назва файлу проста і не стикається з тими викликами, оскільки список буде містити до 20000. І моє питання в основному стосується продуктивності цих двох сегментів. Дякую.
Леон

1
@Leon forПетля буде - на сьогоднішній день - найповільнішою. Інші два методи будуть дуже близькі за швидкістю один до одного.
John1024

7
Крім того, не відкидайте потенційні проблеми: багато питань тут на StackExchange пов’язані з тим, що розщеплення слів або розширення імені траплялося з людьми, які цього не очікували.
John1024

5
Зауважте також, що при читанні файлу з xargsваріантом є --arg-fileваріант : принаймні GNU версія має опцію (коротка форма -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. (Зробіть це зі списком порожніх стислих файлів, щоб виміряти абсолютну кількість накладних витрат.)


0

Наскільки швидко ваш диск?

Для цього слід використовувати всі ваші процесори:

parallel -X gzip -d :::: large_file_list

Тож ваш ліміт, швидше за все, буде швидкістю вашого диска.

Ви можете спробувати налаштувати за допомогою -j:

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.