GNU parallel - это инструмент командной строки для параллельного выполнения заданий.

parallel великолепен и входит в арсенал каждого программиста. Но сначала мне показалось, что документы немного ошеломляют. К счастью, вы можете начать приносить пользу с parallel с помощью всего лишь нескольких основных команд.

Почему parallel так полезен?

Давайте сравним последовательное и параллельное выполнение одной и той же ресурсоемкой задачи.

Представьте, что у вас есть папка с аудиофайлами .wav, которые нужно преобразовать в .flac:

Это довольно большие файлы, каждый размером не менее гигабайта.

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

ffmpeg -i audio1.wav audio1.flac

Давайте напишем сценарий для последовательного преобразования каждого из них:

# convert.sh
ffmpeg -i audio1.wav audio1.flac
ffmpeg -i audio2.wav audio2.flac
ffmpeg -i audio3.wav audio3.flac
ffmpeg -i audio4.wav audio4.flac
ffmpeg -i audio5.wav audio5.flac

Мы можем рассчитать время выполнения задания, добавив time при вызове скрипта из терминала. time будет печатать реальное время, прошедшее во время выполнения.

time ./convert.sh

Наш сценарий завершается через чуть больше минуты.

Неплохо. Но теперь давайте запустим его параллельно!

Нам не нужно ничего менять в нашем сценарии. С флагом -a мы можем направить наш скрипт прямо в parallel. parallel будет запускать каждую строку как отдельную команду.

parallel -a ./convert.sh

При использовании parallel наша конверсия выполнялась чуть более чем за половину времени. Отлично!

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

Я столкнулся с parallel при работе с задачей обработки данных, которая, вероятно, выполнялась бы в течение часа или более, если бы выполнялась последовательно. С parallel это заняло всего несколько минут.

parallel мощность также зависит от вашего компьютера. В моем MacBook Pro Intel i7 всего 4 ядра. Даже эта небольшая задача довела их всех до предела:

Более мощные компьютеры могут иметь процессоры с 8, 16 или даже 32 ядрами, что обеспечивает значительную экономию времени за счет распараллеливания ваших заданий.

Быть полезным с parallel

Другое большое преимущество parallel - его краткость и простота. Давайте начнем с неприятного скрипта Python и преобразуем его в чистый вызов parallel.

Вот скрипт Python для преобразования аудиофайлов:

import subprocess
path = Path.home()/'my-data-here'
for audio_file in list(path.glob('*.wav')):
    cmd = ['ffmpeg',
           '-i',
           str(audio_file),
           f'{audio_file.name.split(".")[0]}.flac']
    subprocess.run(cmd, stdout=subprocess.PIPE)

Ой! На самом деле это большой объем кода, над которым стоит подумать, просто чтобы преобразовать некоторые файлы. (Это занимает около 1,2 минуты).

Давайте преобразуем наш Python в parallel.

Вызов скрипта с parallel -a

parallel -a your-script-here.sh - прекрасный однострочный файл, который мы использовали выше для конвейерной передачи в нашем сценарии bash.

Это замечательно, но требует, чтобы вы написали сценарий bash, который хотите выполнить. В нашем примере мы по-прежнему записывали каждый отдельный вызов ffmpeg в convert.sh.

Трубы и интерполяция строк с parallel

К счастью, parallel дает нам возможность полностью удалить convert.sh.

Вот все, что нам нужно сделать, чтобы совершить конверсию:

ls *.wav | parallel ffmpeg -i {} {.}.flac

Давайте разберемся с этим.

Мы получаем список всех файлов .wav в нашем каталоге с ls *.wav. Затем мы передаем (|) этот список на parallel.

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

Первый - это {}, который parallel автоматически заменяется одной строкой из нашего ввода.

Второй оператор - {.}, который вводит одну строку, но с удаленными расширениями файлов.

Если бы мы расширили команду, выполняемую parallel для нашей первой строки ввода, мы бы увидели ...

ffmpeg -i audio1.wav audio1.flac

Аргументы с Parallel

Как оказалось, нам даже не нужно подключаться к ls, чтобы выполнить нашу задачу. Мы можем пойти еще проще:

parallel ffmpeg -i {} {.}.flac ::: *.wav

Аргументы, переданные в parallel, появляются после команды и разделяются :::. В этом случае нашим аргументом является *.wav, который предоставит список всех файлов .wav в нашем каталоге. Эти файлы становятся исходными данными для нашей молниеносной parallel работы.

Интересный факт: parallel был построен Ole Tange и опубликован в 2011 году. По его словам, вы можете использовать инструмент для исследования, не ссылаясь на исходный текст, за скромную плату в 10 000 евро!

Спасибо за прочтение!