Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Языки программирования. Концепции и принципы

.pdf
Скачиваний:
2
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
Параллельное программирование в Оккаме/2 (модель О)
случае средства инициализации в Аде слабы для установления нужных связей – ведь каждый элемент массива должен получить «имя» партнера по рандеву).
Сравните, в Оккаме члены коллектива связывались каналами именно при опи
сании структуры коллектива (посредством репликаторов).
Поэтому в Аде структуру коллектива процессов приходится создавать динами5
чески (привлекая аппарат динамических параметров и, возможно, ссылок).
Теперь все готово для попытки воплотить в Аде параллельное преобразование
координат.
371
5.8.2. Параллельное преобразование координат в Аде
Вернемся к примеру, рассмотренному ранее для Оккама на стр. 357. Предстоит решить несколько проблем. Покажем их на примере процессов ра.
Статическое объявление процессов
Во5первых, придется описывать коллектив процессов этого вида. Сформируем
его статически, а связывать процессы будем динамически (так как статических средств для этого нет, см. выше). Ясно, что придется ввести массив процессов. Стало быть, нужно описать тип элементов такого массива. Конечно, это должен быть задачный тип Ады. Итак, нужно объявить задачный тип, например тип ра, и массив объектов этого типа.
Во5вторых, нужно понять, какие входы нужны объектам типа ра. Прототип
в Оккаме имел четыре канала, и это соответствовало симметричному рандеву. Так как в Аде рандеву асимметричное, нужно решить, по каким направлениям про цесс будет играть роль мастера, а по каким – клиента, и ввести нужные входы. Примем, что процесс ра предоставляет услуги партнерам сверху (для принятия xj) и партнерам справа (для принятия заготовки yi). При этом он сам пользуется услугами партнера снизу (для передачи ему xj) и партнера слева (для передачи ему модифицированного yi).
Итак, нужны два входа, которые назовем «сверху» и «справа». Запишем толь
ко что разработанный вариант фрагмента программы
task type pa is
entry сверху(X : in real); -- принять xj сверху entry справа(X : in real); -- принять yi справа
end pa;
В теле pa должны быть и операторы приема (этих двух входов), и вызовы вхо
дов (партнеров снизу и слева). Так что тело ра имеет вид
task body pa is
xj, yi,. aij_xj : real; ... accept сверху(X : in real) do -- âåðõ ? xj
xj := x -- прием от партнера сверху end сверху;
372
...
accept справа(Y : in real) do -- право ? yi
yi := x -- прием от партнера справа
end справа;
...
партнер_снизу.сверху(xj); -- íèç ? xj
...-- передача партнеру вниз
партнер_слева.справа(yi); -- ëåâî ? yi
...-- передача партнеру влево
end ðà;
Если сопоставить с текстом ра на Оккаме, сразу видно неудобство записи на Аде «малого параллелизма». Не удается выразить параллелизм внутри процесса ра без новых объявлений задач (со своими входами, параметрами, вызовами входов). Длинно, неудобно, ненаглядно – Ада для такого параллелизма не приспособлена!
Перспективы языков программирования
В5третьих, самый существенный вопрос. Как сообщить процессу типа ра кон кретные имена его партнеров (то есть процессов, с которыми следует связать име на партнерснизу и партнерслева)? Да и значения aij ему также требуется пере дать. Выше объяснено, почему это приходится делать динамически (в частности, при объявлении типа ра процессовпартнеров еще просто нет). Следовательно, при объявлении типа ра необходимо позаботиться о настройке процессов (на связь с конкретными партнерами) в период исполнения программы. Мы пока это го не сделали!
При создании массива из элементов типа ра можно было бы согласованно иници ализировать его элементы, присвоив им подходящие значения. Но для этого нужно иметь возможность «вычислить» нужный процесс и «присвоить» его компоненте массива. Ада не дает возможности «вычислять» процессы нужным образом, да и присваивания ограниченным приватным объектам запрещены, а тем самым и инициализация тоже.
Итак, связать партнеров статически в Аде невозможно (если не все партнеры известны в момент создания программы).
Подчеркнем, что наши проблемы с коммутацией процессов вызваны в значи тельной степени тем, что средства управления рандеву в Аде нельзя отделить от процессов (точнее, от объявления задач). В Оккаме каналы – самостоятельные объекты (предназначенные для связывания других самостоятельных объектов – процессов). Именно разделение этих двух дуальных видов объектов позволяет легко отложить связывание процессов от момента описания процесса (и каналов) до момента порождения конкретного процесса. Канал становится естественным параметром процесса, причем параметром периода компиляции. Нетрудно поро дить столько каналов, сколько нужно для связывания, и указать подходящий ка нал в качестве аргумента процесса. При этом передача аргументаканала каждому партнеру выполняется полностью независимо (при порождении партнера).
Обратите внимание: удачно работает адекватная абстракцияконкретизация (абстракция от партнеров – канал как абстрактное симметричное рандеву, и конк ретизация – настройка канала сначала на одного партнера, затем на другого).
Параллельное программирование в Оккаме/2 (модель О)
373
В Аде именно отсутствие такой абстракции приводит к рассматриваемой нами
сейчас проблеме коммутации. Кстати, появление нужной абстракции вполне воз можно прогнозировать на основе общих критериев качества абстракцииконкре тизации – нужно лишь учесть технологическую потребность в развитой коммута ции процессов.
Организация динамической настройки процессов
Итак, в Аде средств коммутации при порождении процессов нет. Приходится
программировать динамическое связывание партнеров. Но динамическое – зна чит при исполнении программы, а единственное средство связи с работающим процессом в Аде (как и в Оккаме) – рандеву. Причем это обязательно должно быть рандеву родительского процесса со своими потомками – потомки до на стройки еще не могут «знать» друг друга! (
Почему?)
Такое настраивающее рандеву в общем случае должно передать процессу
либо информацию только о его собственных координатах (с тем чтобы сам про цесс «вычислил» своих партнеров), либо непосредственно о самих партнерах. Хотя первое решение в Аде воплотить проще, рассмотрим именно второй вари ант по двум причинам. Вопервых, мы сопоставляем возможности Ады и Окка ма по части коммутации процессов, а в исходной программе на Оккаме процесс вида ра не вычислял своих партнеров, а был настроен на них. Вовторых, не ме нее существенно, что нетрудно так обобщить постановку задачи, чтобы процесс вида ра в принципе не мог вычислить не только конкретного партнера, но даже его тип.
Вопрос. Как это сделать?
Подсказка. В сущности, от процессов, например, вида ру требуется лишь способ
ность воспринимать информацию, передаваемую процессами вида ра.
Итак, будем считать, что при настройке процессу вида ра необходимо передать
информацию, позволяющую ему обратиться к партнеру в общем случае заранее неизвестного вида (типа). Мы пришли к необходимости подправить описание процесса ра – нужен еще один вход для настройки процессов. Его параметрами должны стать aij, а также партнеры снизу и слева.
Принципиальные решения приняты: массив процессов типа ра (двумерный) и
три входа – два для работы, один для настройки. Однако технические проблемы остаются.
Первая техническая проблема – параметрами входа «настройка» в объектах
типа ра могут оказаться объекты того же типа ра. И тип ра становится объявляе мым непосредственно через себя, что в Аде недопустимо. Ведь использовать имя типа до его полного объявления можно лишь в так называемых неполных объяв лениях (предобъявлениях) перед объявлением ссылочных типов. Приходится вводить тип ссылок на ра:
type ðà; type ððà is access pa; type pa is . . .;
374
Перспективы языков программирования
Вторая техническая проблема – для настройки необходимо вычислить ссыл ки на отдельные компоненты массивов процессов (если, как мы и договорились, не заставлять процесс вида ра «знать» о
том, что одни его партнеры организованы в массив определенной структуры и доступ к ним возможен посредством индек сов этого массива, другие партнеры – в массив другой структуры, и т. п.). В Аде есть предопределенная атрибутная функция для вычисления адреса объекта типа Р – P’ADRESS, однако она доставляет не значение нужного типа (а именно рра), а значение типа address из предопределенного пакета SYSTEM. Изза этого при дется заменить массив процессов типа ра массивом ссылок типа рра. Сами про цессы типа ра нужно будет порождать операцией new (то есть динамически).
Так что отсутствие адекватной абстракции (от партнера) привело через по требность в динамической настройке к необходимости динамического порожде ния процессов – все больше нагрузка на целевую машину (столь неприятная для Ады – почему?).
Третья техническая проблема – как учесть «краевые элементы». Ведь «ниж ние» и «левые» процессы типа ра должны общаться с процессами совсем другого типа.
Нам снова не хватает посредников – каналов Оккама. Для них было не важно, с процессом какого типа связать, – лишь бы протоколы были согласованы (кста ти, еще одна полезная абстракция – от типа связываемого процесса). А в Аде ме шает контроль за типами параметров у входа «настройка» (почему?).
Решение можно построить на основе так называемого вариантного комбини рованного типа, дискриминантом которого стал бы «вид_процесса», а выбирае мыми компонентами – процессы разных типов. При этом в самих этих процессах придется разбираться, процесс какого типа оказался партнером слева или снизу, и использовать подходящий селектор записи (единым селектором для процессов разных типов не обойтись – опять же изза контроля типов). Тип процесса (точ нее, его признак) придется задавать при настройке (конечно, вместе с дискрими нантом вариантной записи).
Рассмотрим это решение подробнее (концентрируя внимание в основном на процессах типа ра), хотя в полной мере оно нас удовлетворить заведомо не мо жет – ведь процесс типа ра должен «знать» типы потенциальных партнеров и раз бираться с этими типами самостоятельно. Заодно познакомимся с вариантными типами Ады (в первом приближении вполне аналогичными паскалевским).
Решение с вариантным типом
package координаты_1 is
n : constant integer :=3;
type вид_процесса is(spa, spy, sp0);
type pr; -- предобъявление
type ppr is access pr;
task type pa is
entry настройка(a : in real; слева, снизу : in ppr); entry сверху(X : in real); entry справа(Y : in real);
Параллельное программирование в Оккаме/2 (модель О)
end pa; task type px is
entry настройка(j : in integer; снизу : in pa); end px; task type pb is
entry настройка(bi : in integer; слева : in pa); end px; task type p0 is ...; task type py is ...;
type pr(p : вид_процесса) -- p – дискриминант
record -- вариантного комбинированного типа pr
case p is
when spa=> sa:pa; -- все поля различны! when spy=> sy:py; when sp0=> s0:p0;
end case;
end record;
end координаты_1; package body координаты_1 is
task body pa is
aij, xj, yi, aij_xj : real;
прт-слева, прт-снизу : ppr; -- партнеры begin
accept настройка(a : in real; слева.снизу : in ppr) do
aij := а; прт-слева := слева; прт-снизу := снизу; end настройка; ... accept сверху ...; ... accept справа ...; ... case птр-слева.р is -- рrос_слева(птр-слева, yi);
when spa => прт-слева.sа.справа(уi);
when spy => прт-слева.sy.справа(yi);
when sp0 => PUT("У p0 входа 'справа' нет."); end case;
... case прт-снизу.р is -- рrос_снизу(птр-снизу, xj);
when spa => птр-снизу.sa.свepxy(xj);
when spy => PUT("У py входа 'сверху' нет.");
when sp0 => птр-снизу.s0.cвepxy(xj); end case;
...
end pa;
... -- аналогично (но проще) для px, py, pb, p0
для_ра : array(1..n, 1..n) of ppr; -- только для_ру : array(1..n) of ppr; -- указатели для_р0 : array(1..n) of ppr; -- порождены
375
376
для_рх : array(1..n) of px; -- порождение и запуск процессов; для_рb : array(1..n) of pb; -- ждут настройки на pa
begin -- часть пакета, исполняемая при его загрузке
-- порождение процессов for i in 1..n loop
for j in 1..n loop
äëÿ_ðà(i,j) := new pr(spa);
end loop; -- создано n**2 процессов типа pa и ссылки end loop; -- на них – в переменной для_ра ждут настройки! for i in 1...n loop
äëÿ_ðó(i):= new pr(spy); -- настройка не нужна (почему?) end loop; – ждут рандеву с pa for i in 1...n loop
äëÿ_ð0(i) := new pr(sp0); -- настройка не нужна (почему?) end loop; -- ждут рандеву с ра
-- настройка процессов for i in 1...n loop -- настройка процессов px на верхние ра
для_рх.настройка(j, для_ра(1,j)); -- доступ к ним не нужен, end loop; -- эти процессы сами заказывают рандеву ... -- аналогично предыдущему настройка pb
-- настройка процессов ра for i in 1…n-1 loop -- кроме последней строки
for j in 2..n loop -- кроме первого столбца
для_ра(i,j).sа.настройка(a(i,j), для_ра(i, j-1), для_ра(i+1,j));
end loop; -- коммутация с остальными ра end loop; for j in 2..n loop
для_ра(n,j).sa.настройка(a(n,j), для_ра(n,j–l),
äëÿ_ð0(j)); -- коммутация с p0 end loop; for i in 1..n-l loop
для_ра(i,1).sа.настройка(a(i,l),
äëÿ_ðó(i), -- коммутация с py
äëÿ_ðà(i,1));
end loop;
для_ра(n,1).sа.настройка(a(n.l),
äëÿ_ðó(n), äëÿ_ð0(1)); -- коммутация левого нижнего
end координаты_1;
Перспективы языков программирования
Очевидны нерегулярность и сложность «вариантного» решения. Становится
особенно наглядным различие в уровнях адекватности выразительных средств сущности параллелизма, достигнутых в Оккаме и в Аде. Решая средствами Ады проблемы, ранее успешно решенные средствами Оккама, мы убедились в том, что Адарешение не только приводит к лишним затратам во время исполнения программы, но и существенно сложнее для понимания.
Упражнение (повышенной трудности). Придумайте более изящные Адарешения
рассмотренных проблем.
Подсказка. Сравните свои достижения с решением, изложенным на стр. 379.
Параллельное программирование в Оккаме/2 (модель О)
377
5.9. Перечень неформальных теорем о параллелизме в Аде и Оккаме
Завершим рассмотрение концепции параллелизма в современных ЯП перечнем неформальных теорем, «доказательство» которых фактически представлено в предыдущих разделах. Читателю рекомендуется доказать (или опровергнуть) эти утверждения самостоятельно. Представим сначала утверждения, критикующие концепцию параллелизма в Аде, а затем и концепцию параллелизма в Оккаме.
Критика Ады «со стороны Оккама»
1. Нет естественной симметрии между последовательным и параллельным исполнениями. Другими словами, нельзя придать программе, в которой
требуется и последовательное, и параллельное комбинирование процессов, регулярную структуру – приходится о параллелизме заботиться особо.
2. Нет естественных средств идентификации асинхронных процессов. Ок кам позволяет не изобретать имена для каждого асинхронного процесса. Однако Ада вынуждает это делать. Чтобы прочувствовать, сколь обремени тельно подобное требование, представьте себе, что нужно изобретать имя для каждого оператора в Паскале.
3. Нет естественных (учитывающих структуру программы) выразительных средств для запуска и завершения процессов. И об этом приходится забо титься особо.
4. Нет средств статической коммутации коллектива процессов, точная структура которого неизвестна при создании программы. В Аде статическая коммутация ограничена явным указанием имен входов задач.
5. Нет адекватной абстракции вида рандеву от процессовпартнеров (ср. ка налы и протоколы Оккама).
Критика Оккама «со стороны Ады»
Основная претензия – бедность «обычных» выразительных средств. Например:
1. Нет определяемых программистом типов, нет исключений. Однако самое интересное – сравнить эти ЯП в той области, для которой предназначен Оккам.
2. Нет средств динамической коммутации процессов. Хотя это можно объяснить стремлением к эффективности исполнения (в частности, стремлением возложить распределение процессов по физи ческим процессорам на транслятор), такое ограничение не позволяет рабо тать с коллективами процессов, структура которых становится известной лишь при работе программы. Например, нельзя построить несбалансиро ванное дерево процессов, учитывающее свойства сортируемого потока.
Вопрос. Можно ли это сделать в Аде? Если можно, то как? Подсказка. Мы совсем недавно занимались динамической коммутацией процес
сов средствами Ады.
378
Таким образом, в Оккаме – высокая степень комфорта при программировании
статических коллективов разнородных процессов. Поэтому типизация обмена (протоколы) отделена от процессов и связана с каналами. Ориентация на реаль ную многопроцессорную аппаратуру с эффективным контролем структуры про граммы. Оккамовскую программу можно «спаять» и запустить. И она должна быть максимально надежной. Для этого и нужны протоколы с квазистатическим контролем.
Перспективы языков программирования
5.10. Единая модель
временных расчетов
Некоторые существенные проблемы управления асинхронными процессами ос тались вне нашего рассмотрения. В качестве примера проблемы, оставшейся фак тически открытой и в Аде, и в Оккаме, назовем проблему управления временем исполнения процессов.
Суть проблемы – в том, что программист должен иметь возможность оценить
время исполнения критичных фрагментов программы и рассчитывать на соблю дение определенных гарантий в этом отношении. Такие оценки и гарантии долж ны быть выражены в терминах, независимых от среды исполнения (точнее, среда должна заранее предупреждать о невозможности предоставить требуемые про граммой гарантии).
Ситуация вполне аналогична управлению численными расчетами, однако ни
каких аналогов, касающихся времени исполнения процессов (не только парал лельных), то есть своего рода «единой модели временных расчетов», ни в Аде, ни в Оккаме нет, хотя без ее решения программирование систем реального времени в терминах, независимых от среды исполнения, невозможно. Другими словами, это критичная технологическая проблема для языка реального времени, претен дующего на достаточно высокий уровень абстракции.
Итак, в очередной раз содержательно работают общие критерии качества абст
ракцииконкретизации, позволяя прогнозировать появление в перспективе язы ковых средств, в совокупности предоставляющих в распоряжение программиста единую модель временных расчетов.
Подчеркнем еще раз, что дело не столько в самих средствах, позволяющих уз
навать или заказывать допустимые интервалы времени для исполнения процес сов, сколько в гарантии соблюдения заказанных интервалов в любой реализации ЯП (или обоснованного отказа принять программу к исполнению).
Считаю приятным долгом отметить, что внимание автора к рассмотренной
проблеме было привлечено М. Ж. Грасманисом, занимавшимся проблемами па раллелизма в ЯП под руководством автора в аспирантуре факультета ВМиК МГУ. С другой стороны, приведенная формулировка проблемы, аналогия с мо делью числовых расчетов и соответствующий прогноз принадлежат автору.
Параллельное программирование в Оккаме/2 (модель О)
379
5.11. Моделирование каналов средствами Ады
С учетом критики решения на стр. 374 рассмотрим решение задачи о преобразова нии координат, опирающееся на динамическое моделирование соответствующей Оккампрограммы средствами Ады. Преимущество такого решения – ясность и адекватность сути параллелизма. Наиболее очевидный недостаток – появление дополнительных процессов по сравнению с решением на стр. 374.
Ключевая идея. Ввести задачный тип «канал» в качестве активного мастера
посредника между «рабочими» процессами нашей программы, с тем чтобы все ра бочие процессы стали равноправными клиентами каналов.
Вопервых, отпадет надобность в заказах рандеву с процессами разных типов. Вовторых, отпадет надобность различать рандеву сверхусправа (как активные) и слеваснизу (пассивные) – все рандеву рабочих процессов становятся активны ми. (Кроме настройки! Почему?).
Схема решения на Аде:
1. Создаются коллективы процессов типа «канал». Для связи с ними исполь зуются массивы ссылок соответствующего типа. Каждый канал служит для связывания ровно двух процессов (как в Оккаме).
2. Создаются в нужном количестве процессы типов pa, px, py, pb, p0 и посред ством рандевунастройки с основным запускающим процессом настраива ются на соответствующие каналы.
package координаты_2 is
n : constant integer := 3; task type канал; type на_канал is access канал; task type канал is
entry ввод(X : in real); entry вывод(X : out real);
end канал; верх_низ : array(1..n+l, 1..n) of на_канал; право_лево : array(1..n, 1..n+l) of на_канал; task type pa is
entry настройка(a : in real; сверху, справа, снизу,
слева : in на_канал); end pa; task type px is
entry настройка(j : in integer; снизу : in на_канал); end px; ... -- аналогично для py, p0, pb
end координаты_2; package body координаты_2 is
task body pa is
380
aij, xj, yi, aij_xj : real; наверх, направо, вниз, влево : на_канал;
begin
accept настройка(a : in real; сверху, справа, снизу,
aij := а; наверх := сверху; направо := справа; вниз := снизу; влево := слева;
-- присваивать каналы нельзя (почему?)
-- приходится работать со ссылками на каналы end настройка; ...
íaâepx.ââîä(xj); -- âåðõ ? xj
...
направо.ввод(уi); -- право ? yi
...
вниз.вывод(xj); -- íèç ! xj
...
влево.вывод(уi); -- ëåâî ! yi
...
end pa; äëÿ_ðà : array(1..n, 1..n) of pa;
– процессы pa объявлены статически! Почему так можно?
... аналогично для px, py, pb, p0
begin – начало активного тела пакета
– все "рабочие" процессы запущены и ждут настройки, но каналов еще нет! for i in 1..n+1 loop -- создание каналов верх-низ
for j in 1..n loop
вepx_низ(i,j) := new канал;
end loop;
end loop; -- ждут рабочих рандеву ... аналогично для каналов право_лево
!! НАСТРОЙКА !!
for i in 1..n loop -- настройка px
для_рх(j).настройка(j, верх_низ(1,j));
end loop; -- пошли первые рандеву с каналами for i in 1..n loop -- настройка pb
для_рb.настройка(b(i), право_лево(i, n+1);
end loop; for i in 1..n loop
for j in 1..n loop
для_ра.настройка(a(i,j),
вepx_низ(i,j), право_лево(i,j+1), вepx_низ(i,j+l), право_лево(i,j);
end loop;
end loop;
... аналогично для py и p0
end координаты_2;
... все работает
Перспективы языков программирования
слева : in на_канал) do
-- рандеву с процессом-родителем
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]