№ 103. Массовое выполнение команд в многопроцессорной системе
Во времена, когда вышло первое издание этой книги, многоядерные или многопроцессорные системы были редкостью. В основном к таким системам относились серверы и большие ЭВМ. В наши дни большинство ноутбуков и настольных компьютеров оснащается многоядерными процессорами, позволяющими компьютерам решать несколько задач одновременно. Но некоторые программы не способны использовать дополнительные вычислительные ресурсы и выполняются только на одном ядре; чтобы задействовать несколько ядер, нужно запустить сразу несколько экземпляров программы.
Допустим, что у вас есть программа для преобразования изображений из одного формата в другой и вам требуется обработать большое количество файлов. Последовательное (поочередное, а не параллельное) преобразование всех файлов единственным процессом может занять много времени. Работа завершится намного быстрее, если разбить файлы между несколькими процессами, работающими параллельно.
Сценарий в листинге Б.3 демонстрирует, как распараллелить заданную команду по нескольким процессам, выполняющимся одновременно.
ПРИМЕЧАНИЕ
Если ваш компьютер оснащен одноядерным процессором или выбранная программа работает медленно по другим причинам, например из-за невысокой скорости доступа к жесткому диску, запуск сразу нескольких экземпляров программы только ухудшит производительность. Будьте осторожны, не запускайте слишком много процессов, так как это легко может застопорить маломощную систему. К счастью, даже Raspberry Pi имеет несколько ядер!
Листинг Б.3.Сценарий bulkrun
··#!/bin/bash
··# bulkrun — выполняет обход файлов в каталоге и запускает несколько
··#·· процессов для их одновременной обработки.
··printHelp()
··{
····echo "Usage: $0 −p 3 −i inputDirectory/ −x \"command −to run/\""
····echo −e "\t-p The maximum number of processes to start concurrently"
····echo −e "\t-i The directory containing the files to run the command on"
····echo −e "\t-x The command to run on the chosen files"
····exit 1
··}
··while getopts "p: x: i: " opt
··do
····case "$opt" in
······p) procs="$OPTARG"····;;
······x) command="$OPTARG"··;;
······i) inputdir="$OPTARG";;
······?) printHelp··········;;
····esac
··done
··if [[-z $procs || −z $command || −z $inputdir]]
··then
····echo "Invalid arguments"
····printHelp
··fi
··total=
$(ls $inputdir | wc −l)
··files="$(ls −Sr $inputdir)"
··for k in $(seq 1 $procs $total)
··do
····for i in $(seq 0 $procs)
····do
······if [[$((i+k)) −gt $total]]
······then
········wait
········exit 0
······fi
······file=
$(echo "$files" | sed $(expr $i + $k)"q;d")
······echo "Running $command $inputdir/$file"
······$command "$inputdir/$file"&
····done
····wait
··done
Сценарий bulkrun принимает три аргумента: максимальное количество процессов, действующих в каждый конкретный момент времени
, каталог с файлами для обработки
и команду для запуска (в конец которой добавляется имя файла для обработки)
. После чтения пользовательских аргументов с помощью getopts
сценарий убеждается в наличии всех трех обязательных аргументов. Если любая из переменных −procs, command или inputdir — осталась неопределенной, сценарий выводит сообщение об ошибке
, текст справки и завершает выполнение.
После проверки переменных, необходимых для управления запуском параллельных процессов, сценарий приступает к выполнению фактической работы. Для начала определяется количество обрабатываемых файлов
, и их список сохраняется для дальнейшего использования. Затем сценарий входит в цикл for, который следит за количеством файлов, обработанных к текущему моменту. Команда seq
используется для организации итераций по всем файлам с шагом, равным максимальному числу процессов, которые могут выполняться параллельно.
Читать дальше