3.1. Создание процессов.
Для создания процессов в динамической модели параллелизма используется
стандартная функция NEWTASK. Обращение к этой функции производится
операторами присваивания вида:
it = NEWTASK (n, ia) , где
- n
- - имя программной единицы-задачи;
- ia
- - константа, имя переменной, элемента массива или массива целого типа;
значения этого параметра определяют виртуальные номера вычислителей, на
которых создаются порождаемые процессы.
- it
- - имя переменной, элемента массива или массива типа TASKID, куда
заносятся имена (системные идентификаторы) порожденных процессов.
Функция NEWTASK создает по описанию задачи с именем n вычислительные
процессы, количество которых определяется числом виртуальных номеров
вычислителей. Это число равно количеству элементов массива параметра ia
или равно одному для скалярного параметра.
Результатом выполнения функции NEWTASK является имя процесса или
массив имен (системных идентификаторов) созданных процессов. Эти имена
присваиваются переменным it.
Если n не совпадает с именем программной единицы-задачи, по которой
образован текущий процесс, то оно должно быть описано. По аналогии с
оператором EXTERNAL, для описания имен внешних задач вводится оператор
спецификации TASK EXTERNAL, имеющий вид:
TASK EXTERNAL n [, ni ]...
где n, ni - имена программных единиц-задач.
Имена, указанные в списке этого оператора, разрешается использовать в
качестве фактических параметров процедур (в том числе в качестве
параметров функций NNAME и NEWTASK) и в качестве адресатов в операторе
SEND. Если в качестве таковых параметров используется имя текущей единицы
- задачи, то оно также должно быть описано как внешнее.
Таким образом, передать сообщение процессу можно, идентифицируя его
именем программной единицы-задачи, по которой он был образован (по
имени класса), или по системному имени.
Оператор TASK EXTERNAL должен быть размещен в программе-задаче до
первого исполняемого оператора.
Имена (системные идентификаторы) процессов не имеют внешнего
представления, они скрыты от пользователя, их можно присваивать переменным
типа TASKID. Процесс может определить собственное имя, имя процесса,породившего
его, и имя главного процесса программы с помощью стандартных функций
MYTASKID, РАRENT и МАSTER. Определение имен процессов по номерам виртуальных
процессоров приведено в п. 7.
Созданные процессы загружаются и сразу начинают выполняться на
процессорах,виртуальные (логические) номера которых заданы значениями
параметра ia. Допускается использование одинаковых логических номеров
для размещения процессов на одном виртуальном процессоре (для статической
модели параллелизма это не так !).
Конкретное отображение виртуальных номеров процессоров на процессоры
рабочей машины, способ загрузки процессов определяется реализацией,
например, конструкциями языка описания конфигурации (аналогично тому,
как номера каналов ввода-вывода в стандартах Фортрана не закреплены, а
определяются реализацией, в частности, языком управления заданиями).
Процедура создания главного процесса программы и порядок его размещения
на процессоре также определяется реализацией.
При реализации статической модели параллелизма распределение процессов по
процессорам производиться статически, на этапе подготовки задания. В этом
случае, функции NEWTASK в программе отсутствуют и процессы запускаются при
инициализации задачи. В статическом варианте имена процессов (системные
идентификаторы) определяются с помощью функции GETTASKID, имеются еще ряд
стандартных функций для работы в этом режиме (см. 7.).
Примеры использования функций порождения процессов
Операторы порождения будут иметь окружение программы-задачи С.
TASK PROGRAM C
TASK EXTERNAL A,B,READ,PRINT
TASKID TI, TM(100),TM2(100,2)
INTEGER K,KM(100),KM2(100,2)
.............
END
Инициализация процессов:
1 TI = NEWTASK(A,K)
2 TМ = NEWTASK(A,KМ)
3 TМ2 = NEWTASK(A,KМ2)
DO 4 I=1,100
4 TМ(I) = NEWTASK(A,KМ(I))
DO 5 I=1,100
TМ2(I,1) = NEWTASK(A,KМ2(I,1))
5 TМ2(I,2) = NEWTASK(B,KМ2(I,2))
Операторы с метками 2 и 4 (3 и 5) производят одинаковые
действия, но инициализация в форме 2 (3) предпочтительнее, так
как эта запись позволяет производить инициализацию процессов
параллельно.
Отсутствие в Фортране 77 аппарата вырезок из массивов не
позволяет иметь такую запись для алгоритмов:
DO 6 I=1,99,2
TМ(I) = NEWTASK(A,KМ(I))
6 TМ(I+1) = NEWTASK(В,KМ(I+1))
или
TМ(1) = NEWTASK(READ,KМ(1))
TМ(100) = NEWTASK(PRINT,KМ(100))
DO 7 I=2,99
7 TМ(I) = NEWTASK(В,KМ(I))
В результате выполнения операторов:
TI = NEWTASK(PRINT,1)
8 TI = NEWTASK(READ,2))
на виртуальном процессоре с номером 1 будет образован и запущен процесс
PRINT, внутреннее имя которого потеряно и недоступно программе из-за
выполнения оператора 8.Доступ к нему для передачи сообщений возможен только по
программному имени, заказать от него прием сообщений невозможно. C процессом
на втором процессоре возможен обмен сообщениями в обе стороны.
В языке определена только одна синтаксическая конструкция использования
функции NEWTASK - в операторе присваивания. Тем не менее входные языки могут
разрешать использование этой функции и в других контекстах, там, где
употребляются переменные типа TASKID. Например, оператор:
SEND (NEWTASK(PRINT,MI)) RES
породив процесс печати, передает ему данные через RES.
Виртуальные номера процессоров.
Кодировка виртуальных номеров процессоров произвольная, также как
задание номера меток программы. Эти номера могут быть переданы процессу,
который порождает другие, через каналы ввода, операторами передачи сообщений, а
также сгенерированы программно.
Одинаковые номера процессоров могут быть использованы для оптимального
распределения ресурсов путем выравнивания времени работы процессов. Определив
время работы (число операций) самого время-емкого процесса, можно объединять
на одном процессоре выполнение нескольких других процессов, если их суммарное
время меньше.При этом можно ускорить выполнение программы из-за упрощения
коммуникаций между процессами, выполняемых на одном процессоре.
Виртуальные номера процессоров могут быть использованы для настройки
решающего поля. Параллельные алгоритмы, формулируемые в теоретической
постановке обычно как n-размерные, при программировании приводятся к
параметрам реальной структуры решающего поля. Алгоритмы, реализуемые как
стандартные процедуры, также должны адаптироваться к структуре выделяемых
ресурсов. Настройка структуры (под) программ под параметры вычислительной
среды может управляться заданием номеров виртуальных вычислителей.
При запуске программы на конкретной вычислительной платформе возможно
потребуется сопроводить программу спецификацией об используемых значениях
номеров виртуальных процессоров и схемой их оптимальной дислокации на
вычислителях платформы, и об именах задач, связанных с этими номерами для
последующей загрузки соответствующих процессов. Конкретные реализации,
оговаривая правила загрузки задач на процессоры, могут содержать средства
задания порядка выполнения (приоритета) нескольких процессов на одном
процессоре.
Для статической модели параллелизма балансировка загрузки вычислительной
системы может производиться на уровне языка конфигурации, размещением на
одном физическом процессоре нескольких виртуальных.
4.3. Операторы передачи сообщений
Операторы SEND и RECEIVE имеют вид:
SEND (sm [,ss]...) [список]
RECEIVE (rm [,rs]...) [список]
где
- sm или rm
- - спецификация сообщения - определяет адресата (процесс или процессы,
которым посылается сообщение) или отправителя и способ синхронизации;
- список
- - список передаваемых данных имеет такой же вид, как списки в операторах
ввода/вывода Фортрана 77, т.е. элементом списка может быть константа,имя
переменной, переменная с индексом, имя массива или неявный цикл;
- ss или rs
- - дополнительная спецификация.
Вид спецификации сообщения sm и rm зависит от способа синхронизации.
а) Синхронный способ:
sm есть [TASKID =] адресат,
где адресат есть adr или ALL,
а adr - ссылка на функцию, имя переменной, элемента массива или имя
массива типа TASKID или имя программной единицы-задачи.
Если adr - имя программной единицы-задачи, то сообщение
посылается всем процессам программы, образованным по образцу
указанной программной единицы (исключая посылающую).процедуры.
ALL - означает, что сообщение посылается всем процессам
программы, образованным на момент выполнения оператора передачи
сообщения, исключая процесс-отправитель.
rm есть [TASKID =] t, где t - переменная или элемент массива типа TASKID;
параметр специфицирует процесс - отправитель.
Когда adr (rm) - значение типа TASKID, оно должно ссылаться на
незавершенный процесс.
б) Асинхронный способ
sm есть [TAG=] ie, [TASKID =] адресат
или TASKID = адресат, [TAG =] ie
rm есть [TAG=] ie [,[TASKID =] t]
или TASKID = t, [TAG =] ie
где адресат и t определяются как rm для синхронного способа,
ie - выражение целого типа, значение которого определяет
тег сообщения.
в) Способ без ожидания
sm есть [TASKID =] адресат, FLAG = l
rm есть [TASKID =] adr, FLAG = l
или [FLAG =] l
где
адресат и adr определяется как и для предыдущего способа;
l - имя логической переменной.
Переменная l может использоваться также в стандартных
процедурах MSGDONE(l) и TESTFLAG(l) (см. п.7.). В других ситуациях ее
использование не допускается.
Дополнительная спецификация
Дополнительная спецификация ss и rs , как видно из синтаксиса, является
необязательной.
ss есть ERR = l
и / или SRSTAT = ios
где
- l
- - специфицирует метку, которой передается управление в
случае ошибки при передаче сообщения;
- ios
- - имя переменной или элемента массива целого типа; специфицирует статус
состояния (аналог спецификации IOSTAT в операторах ввода/вывода).
В результате выполнения оператора, переменной ios присваивается
значение O , если не было обнаружено ошибки, и положительное значение,
если обнаружена ошибка. Классификация видов ошибок определяется реализацией
языка. Если спецификация SRSTAT отсутствует, то значение статуса состояния
присваивается системной переменной.
Если спецификация ERR отсутствует, то в случае ошибки, задача (и
вся программа) завершается выполнением стандартной процедуры ABORT.
В качестве дополнительной спецификации rs для оператора RECEIVE,
помимо аналогичных спецификаций ERR и SRSTAT, можно также использовать
необязательную спецификацию SENDER, т.е.
rs есть ERR = l
и / или SRSTAT = i
и / или SENDER = t
где t - переменная типа TASKID.
Cпецификацию SENDER полезно использовать, если отсутствует имя
процесса-отправителя. Переменной t после выполнения оператора RECEIVE
присваивается значение имени процесса-отправителя.
Замечания.
- Способ передачи сообщения и число элементов в списках оператора
SEND и соответствующего оператора RECEIVE должны совпадать; для каждого
элемента - тип, а для массивов также размерность и конфигурация должны
соответствовать друг другу. Конфигурация массива - это одномерный массив целого
типа, размер которого равен размерности исходного массива, а элементы равны
размерам соответствующих измерений. Несоответствие структуры данных при
синхронной передаче считается ошибкой в обоих процессах , при других
способах передачи - ошибкой в задаче-отправителе.
- Если значение переменной адресата есть .NOTASKID. или ссылка
на завершенный процесс, оператор обмена для этого процесса не выполняется, а
спецификация SENDER получает значение .NOTASKID. .
- Не считается ошибкой наличие в почтовом ящике невостребованных
сообщений при завершении процесса.
Использование операторов передачи сообщений
Синхронный режим передачи сообщений
По определению, синхронный режим передачи сообщений требует одновременного
выполнения в процессах соответствующих операторов, что при рассогласовании
работы процессов может быть источником "зависания" программы. Достоинством
данного метода передачи сообщений является принципиальная возможность
проводить обмен данными без использования системных буферов, что может ускорить
время обмена.
Операторы передачи сообщений с пустым списком данных могут
использоваться для синхронизации вычислений программы.
Для этого в процессе, управляющем синхронизацией, следует выполнить
оператор: SEND(ALL), во всех процессах программы - RECEIVE (T1),где Т1
должен ссылаться на управляющую задачу. Управляющий процесс продолжиться
только после выполнения во всех задачах оператора RECEIVE. Оператор
SEND(А) требует синхронизации только от процессов, образованных по
программе-задаче А, а SEND(ТМ) - от процессов, на которые ссылаются элементы
массива ТМ. Если процессам известно, что рандеву будет требовать начальный
процесс, то их синхронизирующий оператор может быть записан: RECEIVE
(МАSТЕR()). Точная синтаксическая запись:
Т1=МАSTER()
RECEIVE (T1)
так как в параметрах операторов передачи сообщений не разрешается
выражений. Реализации могут иметь расширенное толкование
семантики, в частности, допускать использование функций типа TASKID в
позициях переменных этого типа. Некоторые реализации могут запрещать в
операторах приема сообщений неявные циклы, управляемые параметром, находящимся
в этом же списке ввода. Так, в Фортране МВК запрещены такие конструкции:
RECEIVE (<..>) N, (KM(I),I=1,N)
Асинхронный режим передачи сообщений
Использование данного способа обмена сообщениями делает программу
еще менее критичной к согласованию операторов обмена сообщениями, так
как процесс-отправитель продолжает работу после передачи сообщения, не
дожидаясь конца фактической передачи сообщения (и даже начала, так как
система интерпретации "должна" сразу же, скопировав передаваемые данные в
буфер, "отпустить" процесс) . Получатель сообщений этого типа может выдавать
директиву приема сообщения, только удостоверившись в наличии нужного
сообщения в почтовом ящике процесса при помощи функций: TESTMSG,TESTTAG,PROBE.
Выполнение оператора RECEIVE без проверки наличия сообщения в почтовом ящике
процесса, по аналогии с синхронным способом обмена, приводит к задержки
выполнения процесса до приема сообщения с заказанным тегом и ,возможно, с
заданным именем отправителя. Но в отличие от синхронного способа, асинхронный
способ позволяют проводить селекцию поступающих сообщений при помощи
конструкций выбора. Пусть процесс может получать сообщения с тегом 1, но от
процесса TI - скаляр целого типа, а от процесса TM(1) - массив из 100 целых
чисел. Тогда,прием таких сообщений может быть запрограммированно так:
IF (TESTTAG(1)) THEN
SELECT MESSAGE
CASE(1,TI)
RECEIVE (1) K
CASE(1,TM(1))
RECEIVE (1) KM
END SELECT
END IF
или:
SELECT MESSAGE
CASE(1,TI)
RECEIVE (1) K
CASE(1,TM(1))
RECEIVE (1) KM
CASE DEFAULT
GO TO 2
END SELECT
2 CONTINUE
Ожидание этих сообщений, то есть, по аналогии с синхронной передачей
сообщений, прерывание работы процесса до получения сообщения (в
данном случае любого из ожидаемого) записывается так:
SELECT MESSAGE
CASE(1,TI)
RECEIVE (1) K
CASE(1,TM(1))
RECEIVE (1) KM
END SELECT
Если сообщения различаются тегами, то конструкция их ожидания может иметь
вид:
SELECT MESSAGE
CASE(1)
RECEIVE (1) K
CASE(2,TM(1))
RECEIVE (2) KM
END SELECT
Режим передачи сообщений без ожидания
Передачи сообщений этого типа рекомендуется использовать для передачи
данных "впрок", заблаговременно. Если после операторов SEND / RECEIVE без
ожидания, поместить операторы:
CALL MSDONE(L),
семантика операторов будет совпадать с семантикой асинхронных операторов.
Следует иметь ввиду, что при использовании операторов данного вида в цикле
без использования процедур ожидания фактической передачи сообщений возможно
искажение передаваемых данных; семантика функции TESTFLAG при этом будет
двусмысленной.
Структура элементов списка передаваемых сообщений
Структура элементов списка передаваемых (принимаемых) сообщений,
как объявлено, совпадает с списком ввода вывода операторов обмена
Фортрана. Широкие возможности предоставляет аппарат неявных циклов.
Пусть имеются описания: REAL A(10),B(5,5). Тогда оператор:
SEND(ALL)A,(B(I),I=1,5),(B(I,1),I=1,5),(B(I,I),I=1,5)
перешлет всем процессам программы следующие объекты:
- массив А целиком,
- первый столбец массива В,
- диагональные элементы массива В
Этим способом можно организовать не только передачу (прием) любых
вырезок их массивов, но и множества одинаковых данных, например, переслать
пять копий массива В: SEND(ALL)(B,I=1,5)
7.2. Использование статической модели параллелизма
Пусть, необходимо написать параллельную программу суммирования
элементов массива, которая сама настраивалась на число процессоров,
выделяемых ей для работы. Программа суммирования может быть записана
так:
TASK PROGRAM SUM
C Параметризация максимальной длины суммируемого массива и тега
PARAMETR (NA=100000,NT=13)
REAL A(NA)
TASKID T
C Ожидание вызова, N - длина массива, подлежащая суммированию
C Фиксация в T имени вызывающего процесса, асинхронно
1 RECEIVE (TAG=NT,SENDER=T) N
C Прием суммируемого массива по синхронному протоколу
RECEIVE (TASKID=T) (NA(I),I=1,N)
S = 0.0
DO I = 1,N
S = S + NA(I)
ENDDO
C Отсылка результата в вызывающий процесс, асинхронно
SEND (TAG=NT+1,TASKID=T) S
C Переход в начало для многократного суммирования
GO TO 1
END
Обращение к процессу суммирования может многократно производится по
имени процесса SUM из любого процесса программы последовательностью
операторов:
SEND (SUM,TAG=NT) N
SEND (SUM) (A(I),I=1,N)
RECEIVE (TAG=NT+1) S
Естественно, значение NT должны быть согласовано с значением тега
в программе SUM. В вызывающем процессе должны находиться описания
A и SUM ( TASK EXTERNAL SUM), определено значение N.
Модификация программы для параллельного суммирования с коэффициентом
распараллеливания, равным числу инициализированных при задании
конфигурации процессов класса SUM, может быть проведена так:
В вызывающем процессе описывается массив MCPU целого типа с длиной,
равной максимальному числу виртуальных процессоров, на которых возможна
инициализация процессов класса SUM.
Перед обращением определяется число процессов SUM и квота элементов
массива суммирования для каждого процесса SUM (общая его длина
принимается за N).
NSUM = NVIRPR(SUM,MCPU,NNAME(SUM))
C Вычисление квоты для суммируемых сегментов массива
N1 = N/NSUM
N2 = N1
SSUM = 0.0
DO J=1,NSUM
C Последняя длина может быть длиннее N1, если N не кратно NSUM
IF (J.EQ.NSUM) N2 = N-(J-1)*N1
SEND (TASKID=GETTASKID(MCPU(J)),TAG=NT) N2
SEND (TASKID=GETTASKID(MCPU(J)))(A(I),I=1+J*N1-N1,1+J*N1-N1+N2)
SELECT MASSAGE
CASE (TAG=NT+1)
RECEIVE (TAG=NT+1) S
SSUM = SSUM+S
END SELECT
ENDDO
Манипулируя только числом порожденных процессов SUM при задании
конфигурации можно, не изменяя программы, при ее исполнении менять
степень распараллеливания суммирования. В данном примере функции распределения
работ были отданы вызывающей программе.
Для управления параллельными работами в самой процедуре суммирования (или
иной распараллеливаемой процедуре) можно использовать другие приемы
программирования.
Пусть, вызывающая программа отдает массив(ы) для обработки одной
процедуре (процессу), которая сама будет распараллеливать процесс вычислений
по своему алгоритму, с учетом наличия в вычислительной среде однородных
процессов. Следующий оператор и его окружение в вызывающей программе
очевиден.
NSUM = NVIRPR(SUM,MCPU,NNAME(SUM))
Далее, процесс отправляет первому (возможно единственному) массив для
обработки и принимает результаты.
SEND (TASKID=GETTASKID(MCPU(1)),TAG=NT) N
SEND (TASKID=GETTASKID(MCPU(1)))(A(I),I=1,N)
RECEIVE (TAG=NT+1) S
Теперь необходима модификация описания процедуры SUM.
TASK PROGRAM SUM
PARAMETR (NA=100000,NT=13,MAXCPU(100))
REAL A(NA)
TASKID T
INTEGER MCPU(MAXCPU)
NSUM = NVIRPR(SUM,MCPU,NNAME(SUM))
1 IF (MCPU(1).NQ.MYVR()) GO TO 2
RECEIVE (TAG=NT,SENDER=T) N
RECEIVE (TASKID=T) (NA(I),I=1,N)
N1 = N/NSUM
N2 = N1
SSUM = 0.0
DO J=2,NSUM
IF (J.EQ.NSUM) N2 = N-(J-1)*N1
SEND (TASKID=GETTASKID(MCPU(J)),TAG=NT) N2
SEND (TASKID=GETTASKID(MCPU(J)))(A(I),I=1+J*N1-N1,1+J*N1-N1+N2)
SELECT MASSAGE
CASE (TAG=NT+1)
RECEIVE (TAG=NT+1) S
SSUM = SSUM+S
END SELECT
ENDDO
DO I = 1,N1
SSUM = SSUM + NA(I)
ENDDO
SEND (TAG=NT+1,TASKID=T) SSUM
GO TO 1
2 RECEIVE (TAG=NT,SENDER=T) N
RECEIVE (TASKID=T) (NA(I),I=1,N)
S = 0.0
DO I = 1,N
S = S + NA(I)
ENDDO
SEND (TAG=NT+1,TASKID=T) S
GO TO 1
END