четверг, 5 августа 2010 г.

Параллельное выполнение команд: GNU Parallel

Проект GNU Parallel развивается с 2007 года и представляет собой утилиту для выполнения задач в shell параллельно в текущей и/или удаленных системах. Задачи формируются из скриптов и команд операционной системы, и параметров (или частей команд) из стандартного ввода. В этом GNU Parallel похож на xargs, в задачи которого входит формирование команд из данных со стандартного ввода, по заявлению разработчика, GNU Parallel даже имеет схожие с xargs опции.
Схожесть с xargs позволяет использовать GNU Parallel в ранее созданных скриптах, в сложных конвейерах, используя все предоставленные ресурсы имеющихся систем (ядра/процессоры текущей или удаленных многоядерных/многопроцессорных систем).

Задачи, в решении которых целесообразно использовать GNU Parallel:
  • Многократное выполнение операций над группой файлов (перекодирование аудио/видео, обработка текста). Важным критерием является продолжительность одной операции. Чем больше времени требуется на выполнение одной операции, тем лучший результат даст использование GNU Parallel;
  • Расчет мат. задач по подготовленному плану (в виде последовательности shell-команд).

Установка
Разработчик GNU Parallel предлагает выполнить установку командой ./configure && make && make install в директории с исходными кодами утилиты. У меня есть другое решение.
GNU Parallel в текущей редакции представляет собой один скрипт parallel на perl. Я предлагаю не выполнять make install, а проделать следующее, в случае если вы используете bash в качестве shell'а:
  • Создать директорию ~/.bin - здесь будут размещаться исполняемые файлы доступные пользователю ;
  • Скопировать файл parallel в ~/.bin ;
  • Добавить в конец файла ~/.bash_profile строку PATH=$PATH:/home/USERNAME/.bin - т.е. добавить к переменной PATH путь к исполняемым файлам, доступным пользователю. Возможно потребуется также править ~/.bashrc .
Таким образом, можно установить GNU Parallel на любую машину с unix-подобной ОС, установленным bash и perl, при наличии лишь прав на запись и выполнение в домашней директории.

Пример 1
Задача: Перекодировать все mp3 файлы в текущей директории в wav.
Ресурсы:
Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz (2 ядра).
Решение:
$ find *.mp3 -print0 | /usr/bin/time xargs -0 -n1 -I{} lame {} -o {}.wav

591.59user 1.73system 9:54.96elapsed 99%CPU (0avgtext+0avgdata 9424maxresident)k 0inputs+0outputs (0major+28668minor)pagefaults 0swaps
Решение с использованием GNU Parallel:
$ /usr/bin/time parallel -j+0 lame {} -o {.}.wav ::: *.mp3

593.11user 1.94system 5:05.27elapsed 194%CPU (0avgtext+0avgdata 24208maxresident)k 0inputs+0outputs (0major+68797minor)pagefaults 0swaps
Вывод:
GNU Parallel в решении данной задачи более эффективно использовал предоставленные ресурсы (194%CPU против 99%CPU), что позволило сократить время решения задачи с ~10 минут до ~5 минут. P.S. все файлы были размещены на ramfs.

Пример 2
Задача: Факторизовать числа от 17999999900000 до 17999999999999.
Ресурсы:
Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz (2 ядра).
Celeron (Coppermine) 791MHz (1 процес., удал. система).
2 ядра удал. системы.
Замечание:
В данном случае нас интересует время решения задачи, а не собственно разложение на множители, поэтому создадим ~/.bin/myfactor - утилита на базе утилиты factor, факторизующая число, но не выводяющая результат в стандартный вывод. Утилита myfactor должна быть распространена на все удаленные системы, которые будут участвовать в решении задачи.
~/.bin/myfactor:
#!/bin/sh

factor $* > /dev/null
$ ls -l ~/.bin/myfactor
-rwxr-xr-x 1 USERNAME USERNAME 33 Авг 4 20:42 /home/USERNAME/.bin/myfactor
Решение:
$ seq 17999999900000 17999999999999 | /usr/bin/time xargs -n1 myfactor

224.74user 69.58system 6:24.55elapsed 76%CPU (0avgtext+0avgdata 4736maxresident)k 0inputs+0outputs (0major+67839100minor)pagefaults 0swaps
Решение с использованием GNU Parallel:
Чтобы наиболее эффективно решить данную задачу, задействовав все предоставленные ресурсы, необходимо использовать возможность GNU Parallel соединяться с удаленными системами через ssh-протокол. Существуют несколько способов сообщить GNU Parallel о конфигурации удаленных систем, я использовал параметр --sshloginfile, который ссылался на файл ~/parallelservers, где были описаны все удаленные системы.
Тут стоит заметить, что использовать возможность GNU Parallel соединяться с удаленными системами целесообразно, только если включен механизм OpenSSH, называемый ControlMaster. Иначе, GNU Parallel связывает с каждой командой новую ssh-сессию, что негативно сказывается на общем времени выполнения.
Также, стоит обратить внимание на опцию -j+0, в случае её отсутствия, независимо от содержимого ~/parallelservers, на удаленных системах будет запущено по 9 (default) задач и по 9 (и больше) ssh-соединений. В большинстве случаев это приводит к отключению от sshd с ошибкой, соответствующей превышению лимита соединений.
$ seq 17999999900000 17999999999999 | /usr/bin/time parallel -j+0 --sshloginfile ~/parallelservers myfactor

249.12user 183.46system 5:00.61elapsed 143%CPU (0avgtext+0avgdata 24000maxresident)k 0inputs+0outputs (0major+106855307minor)pagefaults 0swaps
Вывод:
GNU Parallel использовал 2 ядра текущей системы (143%CPU против 76%CPU), а также все доступные ресурсы удаленных систем, что в итоге позволило решить задачу на ~1.5 минуты быстрее.

Общая схема использования
Если перейти по приведенным выше ссылкам, то можно ознакомится с примерами разработчика по использованию GNU Parallel. Я считаю, что помимо примеров по использованию этой утилитой полезно будет рассмотреть общую схему использования.
Я вижу общую схему использования GNU Parallel так:
  • Установить GNU Parallel предложенным выше способом на все машины, где у вас есть ssh-доступ;
  • Составить список всех доступных машин в файле, путь к которому будет указываться параметром --sshloginfile;
  • Включить механизм ControlMaster;
  • Разбивать сложную (и требующую длительного решения) задачу на несколько длительных подзадач;
  • Выполнить задачу, разбитую на подзадачи при помощи GNU Parallel.
Плюсы от использования данной схемы:
  • Использование GNU Parallel схоже с использованием xargs, что упрощает построение параллельных команд;
  • Использование всех доступных ресурсов текущей системы (все ядра и процессоры);
  • Чтобы получить в свое распоряжение ресурсы удаленных систем (bash+perl), достаточно иметь ssh-доступ и право на запись и выполнение в домашней директории.
Последнее выгодно отличает GNU Parallel и эту схему, в частности, от использования сложных инструментов, и позволяет распараллеливать задачу обычному пользователю без установки дополнительных демонов, сторонних ядер, написании сложного кода. Однако GNU Parallel и эта схема, в частности, применима для решения существенно меньшего количества задач.

Ссылки

2 комментария:

  1. Интересный тул, но есть гораздо более мощный инструмент ;-)
    http://sf.net/projects/paexec/

    Правда, он не совместим по опциям с xargs, соответственно не так удобен для простых задач. Совместимость с xargs сделаю в виде отдельной команды paxargs в следующей версии paexec.

    ОтветитьУдалить
  2. To cheusov,
    Спасибо за ссылку, будет время, попробую.
    P.S. сборка пакета не на BSD довольно утомительна...

    ОтветитьУдалить