Илья Аввакумов, Freepascal.ru
Статья посвящена вопросу написания распределенных (параллельных) вычислений с использованием компилятора FreePascal (использовалась версия 2.0.1)
Проблема параллельных вычислений заинтересовала меня совсем не потому что это сейчас модно. Столкнулся с задачей, когда надо было сформировать (для дальнейнего анализа) большой массив данных. Хотелось уменьшить время вычислений имеющимися средствами. Оказывается, организовать параллельные вычисления с использованием моего любимого компилятора — вполне решаемая задача.
Стандартом для параллельных приложений для многопроцессорных вычислительных систем де-факто является MPI.
Идея MPI-программы такова: параллельная программа представляется в виде множества взаимодействующих (посредством коммуникационных процедур MPI) процессов.
Параллельные вычисления требуют
1. Разделения процессов
2. Взаимодействия между ними
MPI (Message Passing Interface) — стандарт на программный инструментарий для обеспечения связи между ветвями параллельного приложения.
В этой статье рассматривается MPICH (MPI CHameleon), свободно распространяемая реализация MPI. Использовалась версия MPICH 1.2.5 для Windows.
MPICH для Windows требует
1. Windows NT4/2000/XP ( Professional или Server). Под Win9x/ME работать не станет!
2. Сетевое соединение по протоколу TCP/IP между машинами.
Сразу обговорю, что все примеры тестировались на двух машинах, объединенных в локальную сеть. Один компьютер (сетевое имя ILYA) — мой, а второй (сетевое имя EKATERINA) — жены.
Установка.
Компьютеры, участвующие в вычислениях, назовем кластером. MPICH должен быть установлен на каждом компьютере в кластере.
Для установки нужно
1. Скачать mpich.nt.1.2.5.src.exe (5278 Кб) или mpich.nt.1.2.5.src.zip (5248 Кб)
Либо с официальной страницы MPICH
http://www.mcs.anl.gov/mpi/mpich/download.html
Либо с ftp сервера ftp.mcs.anl.gov/pub/mpi/nt.
2. Если запустить exe файл, то после распаковки запустится интерактивная программа установки MPICH. Чтобы не утомлять себя выбором устанавливаемых компонент, удобнее установить MPICH в неинтерактивном режиме.
Для этого
а. Разархивируйте содержимое в общую папку (например, \ILYA\common)
b. Отредактируйте файл setup.iss
c. Строка
szDir=C:\Program Files\MPICH
определяет каталог, куда установится MPICH. Это расположение можно изменить.
d. Строки
Component-count=7
Component-0=runtime dlls
Component-1=mpd
Component-2=SDK
Component-3=Help
Component-4=SDK.gcc
Component-5=RemoteShell
Component-6=Jumpshot
определяют число устанавливаемых компонент. Для главного компьютера (откуда запускается главный процесс) подходящие опции таковы
Component-count=4
Component-0=runtime dlls
Component-1=mpd
Component-2=SDK
Component-3=Help
Для простого компьютера (которому отводится только роль вычислителя) число компонент может быть сокращено до двух.
Component-count=2
Component-0=runtime dlls
Component-1=mpd
На каждом компьютере кластера выполнить команду установки в неинтерактивном режиме. В моем случае запуск программы установки таков:
>\ILYA\common\setup -s -f1\ILYA\common\setup.iss
После установки на каждом компьютере должна запуститься служба mpich_mpd (MPICH Daemon (C) 2001 Argonne National Lab). (смотрите рисунок)
Если был установлен компонент SDK (что необходимо сделать на том компьютере, откуда будет производиться запуск программ), то в каталоге MPICH (прописанном в пункте szDir) присутствуют подкаталоги SDK и SDK.gcc. Содержимое этих каталогов — библиотечные и заголовочные файлы для языков C, С++ и Fortran.
Каталог SDK предназначен для компиляторов MS VC++ 6.x и Compaq Visual Fortran 6.x, а каталог SDK.gcc — для компиляторов gcc и g77.
Настройка
Настройку можно осуществить с помощью простых утилит, имеющихся в дистрибутиве.
Остановимся подробнее на каталоге mpd\bin в директории MPICH. Содержимое каталога:
mpd.exe | исполняемый файл службы mpich_mpd | нужна |
MPIRun.exe | файл, осуществляющий запуск каждой MPI-программы. | нужна |
MPIRegister.exe | программа для шифрования паролей при обмене данными по LAN. | иногда полезна |
MPDUpdate.exe | программа для обновления библиотек MPI | не нужна |
MPIConfig.exe | программа настройки хостов в кластере | не нужна |
guiMPIRun.exe | GUI версия mpirun. | не нужна |
MPIJob.exe | программа для управления MPI-процессами | не нужна |
guiMPIJob.exe | GUI версия mpijob.exe | не нужна |
Использование команд mpirun и mpiregister ждет нас впереди. Чтобы удостовериться, что службы MPICH, работающие на разных компьютерах, взаимодействуют должным образом, можно воспользоваться утилитой MPIconfig. Для этого следует
1. Запустить MPIConfig.exe (можно воспользоваться ссылкой в главном меню, она там должна быть)
2. Нажать на кнопку "Select"
3. В появившемся окне выбрать пункт меню "Action"—"Scan hosts"
4. Напротив имени каждой машины должна загореться пиктограмма "MPI" ( примерно вот так)
Модуль mpi на FreePascal.
Все вышеописанное относилось к установке собственно MPICH. Для того, чтобы прикрутить библиотеки MPICH к FreePascal, следует еще немножко поработать.
Cледует воспользоваться динамической библиотекой mpich.dll, которая располагается в системном каталоге (копируется туда при установке MPICH).
1. Скачать модуль FreePascal, реализующий функции этой динамической библиотеки. Файл mpi.pp скачать zip-архив (10 КБ)
2. Для использования модуля mpi следует просто скопировать файл mpi.pp в каталог, где FreePascal ищет модули (unit searchpath).
Модуль написан с использованием утилиты h4pas.exe и заголовочных файлов *.h из SDK\Include.
Простейшая MPI программа на FreePascal.
Во именах всех функциях библиотеки MPICH используется префикс MPI_. Возвращаемое значение большинства функций — 0, если вызов был успешным, а иначе — код ошибки.
Основные функции.
Основные функции MPI, с помощью которых можно организовать параллельное вычисление
1 | MPI_Init | подключение к MPI |
2 | MPI_Finalize | завершение работы с MPI |
3 | MPI_Comm_size | определение размера области взаимодействия |
4 | MPI_Comm_rank | определение номера процесса |
5 | MPI_Send | стандартная блокирующая передача |
6 | MPI_Recv | блокирующий прием |
Утверждается, что этого хватит. Причем первые четыре функции должны вызываться только один раз, а собственно взаимодействие процессов — это последние два пункта.
Описание функций, осуществляющих передачу, оставим на потом, а сейчас рассмотрим описание функций инициализации/завершения
function MPI_Init( var argc : longint;
var argv : ppchar) : longint;
Инициализация MPI. Аргументы argc и argv — переменные модуля system, определяющие число параметров командной строки и сами эти параметры, соответственно.
При успешном вызове функции MPI_Init создается коммуникатор ( область взаимодействия процессов), под именем MPI_COMM_WORLD.
function MPI_Comm_size( comm : MPI_Comm;
var nump : longint) : longint;
Определяет число процессов, входящих в коммуникатор comm.
function MPI_Comm_rank( comm : MPI_Comm;
var proc_id : longint) : longint;
Определяется ранг процесса внутри коммуникатора. После вызова этой функции все процессы, запущенные загрузчиком MPI-приложения, получают свой уникальный номер (значение возвращаемой переменной proc_id у всех разное). После вызова функции MPI_Comm_rank можно, таким образом, назначать различным процессам различные вычисления.
functionnn MPI_Finalize : longint;
Завершает работу с MPI.
Порядок вызова таков:
1. MPI_Init — подключение к MPI
2. MPI_Comm_size — определение размера области взаимодействия
3. MPI_Comm_rank — определение номера процесса
4. Далее идет любая совокупность команд обмена (передача, прием, и тп.)
5. MPI_Finalize — завершение работы с MPI
Простейшая MPI программа такова.
test.pas
uses mpi;
var namelen, numprocs, myid : longint;
processor_name : pchar;
begin
MPI_Init( argc, argv);
MPI_Comm_size( MPI_COMM_WORLD, numprocs);
MPI_Comm_rank( MPI_COMM_WORLD, myid);
GetMem( processor_name, MPI_MAX_PROCESSOR_NAME+1); // константа MPI_MAX_PROCESSOR_NAME равна 256
namelen := MPI_MAX_PROCESSOR_NAME;
MPI_Get_processor_name( processor_name, namelen);
Writeln('Hello from ',myid,' on ', processor_name);
FreeMem(processor_name);
MPI_Finalize;
end.
Здесь, как видно, никакого обмена нет, каждый процесс только "докладывает" свой ранг.
Для наглядности выводится также имя компьютера, где запущен каждый процесс. Для его определения используется функция MPI_Get_processor_name.
function MPI_Get_processor_name( proc_name : Pchar;
var name_len : longint) : longint;
При успешном вызове этой функции переменная proc_name содержит строку с именем компьютера, а name_len — длину этой строки.
После компиляции (с соответствующими опциями)
>fpc -dRELEASE [-Fu<каталог, где размещен файл mpi.pp>] test.pas
должен появиться исполняемый файл test.exe, однако рано радоваться. Запуск этого exe-файла не есть запуск параллельной программы.
Запуск MPI-программы осуществляется с помощью загрузчика приложения mpirun. Формат вызова таков:
>mpirun [ключи mpirun] программа [ключи программы]
Вот некоторые из опций команды mpirun:
-np x |
запуск x процессов. Значение x может не совпадать с числом компьютеров в кластере. В этом случае на некоторых машинах запустится несколько процессов. То, как они будут распределены, mpirun решит сам (зависит от установок, сделанных программой MPIConfig.exe) |
-localonly x |
-np x -localonly |
запуск x процессов только на локальной машине |
-machinefile filename |
использовать файл с именами машин |
-hosts n host1 host2 ... hostn |
-hosts n host1 m1 host2 m2 ... hostn mn |
запустить на n явно указанных машинах. Если при этом явно указать число процессов на каждой из машин, то опция -np становится необязательной |
-map drive: \host\share |
использовать временный диск |
-dir drive:\my\working\directory |
запускать процессы в указанной директории |
-env "var1=val1|var2=val2|var3=val3..." |
присвоить значения переменным окружения |
-logon |
запросить имя пользователя и пароль |
-pwdfile filename |
использовать указанный файл для считывания имени пользователя и пароля.Первая строка в файле должна содержать имя пользователя, а вторая — его пароль) |
-nocolor |
подавить вывод от процессов различным цветом |
-priority class[:level] |
установить класс приоритета процессов и, опционально, уровень приоритета.class = 0,1,2,3,4 = idle, below, normal, above, high level = 0,1,2,3,4,5 = idle, lowest, below, normal, above, highest |
по умолчанию используется -priority 1:3, то есть очень низкий приоритет. |
Для организации параллельного вычисления на нескольких машинах следует