Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Языки программирования. Концепции и принципы
.pdf
Реляционное программирование (модель Р)
щаются в правила порождения (левая часть – в последовательность фильтров,
то есть правила анализа; правая часть – в следствия, то есть правила синтеза).
Третья модификация – отменяется последовательный перебор правил – они
работают все сразу и не над ведущим термом, а над всем модифицированным «по
лем зрения» – базой данных. Вот и все.
Итак, реляционный стиль программирования позволяет решать задачи на но5
вом уровне разделения труда между человеком и компьютером.
• Человек описывает мир (представляет знания о некоторой предметной об
ласти в базе знаний).
• Человек ставит задачу (формулирует запрос к базе знаний).
• Компьютер самостоятельно решает задачу, используя известные ему факты
и соотношения (правила вывода).
Можно сказать и так, что человек создает и представляет в БЗ теорию предмет
ной области (как мы создали «теорию родственных отношений»). Затем (обычно
другой человек) формулирует теорему (существования решения некоторой со
держательной задачи, например теорему существования дяди у Кузьмы). Нако
нец, компьютер доказывает эту теорему, предъявляя решение задачи (то есть Сте
пана в случае нашей задачи).
В связи с такой терминологией реляционное программирование называют час
то логическим. Логическая терминология оправдана также тем, что в этой облас
ти действительно применяют методы доказательства теорем, в частности метод
резолюций. Его характерная особенность: при подборе сопоставимого следствия
выбирается наиболее общая согласующая подстановка.
С другой стороны, реляционное программирование обеспечивает абстракцию от
программы, требуя от пользователя БЗ лишь постановки задачи (запроса). Такая
абстракция полезна, например, для асинхронной (параллельной) реализации реля
ционного языка. Скажем, в очередном цикле развертка каждого правила может
выполняться совершенно независимо над БЗ, используемой только для чтения.
Нетрудно предвидеть развитие правил до активных процессов из определен
ных классов, работающих над единым представлением знаний о мире. Такие про
цессы естественно трактовать как объекты, обменивающиеся сообщениями друг
с другом (например, чтобы поручить решение подзадачи) и, в частности, с БЗ (на
пример, чтобы узнать некоторый факт). Остается добавить концепцию наследова5
ния, и получим мостик к объектно5ориентированному программированию.
Еще одно перспективное направление, которое интенсивно развивается, – со
временные «языки представления знаний». Заинтересованного читателя отсыла
ем к литературе [27, 28].
351


Глава 5
Параллельное
программирование
в ОккамеE2 (модель О)
5.1. Принципы параллелизма
в Оккаме ........................................ 354
5.2. Первые примеры
применения каналов...................... 355
5.3. Сортировка конвейером
фильтров ....................................... 356
5.4. Параллельное преобразование
координат (умножение вектора
на матрицу) ................................... 357
5.5. Монитор Хансена!Хоара
на Оккаме!2 ................................... 362
5.6. Сортировка деревом
исполнителей ................................ 363
5.7. Завершение работы
коллектива процессов ................... 366
5.8. Сопоставление концепций
параллелизма в Оккаме и в Аде ..... 368
5.9. Перечень неформальных
теорем о параллелизме в Аде
и Оккаме ........................................ 377
5.10. Единая модель временных
расчетов ........................................ 378
5.11. Моделирование каналов
средствами Ады ............................ 378
5.12. Отступление о задачных
и подпрограммных (процедурных)
типах .............................................. 381

354
Перспективы языков программирования
5.1. Принципы параллелизма в Оккаме
Одно из наиболее значительных достижений последнего времени в области ин
форматики – появление процессоров фирмы Инмос, специально предназначен
ных для объединения в высокопроизводительные вычислительные среды и полу
чивших название транспьютеров. Характерно (а для нас особенно интересно), что
одновременно с транспьютерами фирма разработала язык параллельного прог
раммирования (ЯПП) и его реализацию, обеспечивающую возможность абстра
гироваться от реального набора доступных процессоров. Другими словами, если
отвлечься от скорости исполнения, то работа программы не зависит от количества
транспьютеров (его можно учесть при трансляции программы – транслятор вы
полнит подходящую конкретизацию).
Важнейшая цель такого ЯПП – преодолеть представление о том, что програм
мировать асинхронные процессы исключительно сложно. Иначе трудно надеять
ся на коммерческий успех транспьютеров.
Поставленной цели удалось добиться построением языка на основе принци
пов, предложенных одним из самых ясно мыслящих теоретиков информатики
Тони Хоаром. Язык получил название «Оккам» в честь средневекового философа
Уильяма Оккама (ок. 1285–1349), известного, в частности, так называемой брит
вой Оккама. Этот принцип логических построений можно сформулировать так:
делай как можно проще, но не более того. Бритва Оккама отсекает все лишнее в ЯП
так же успешно, как принцип чемоданчика.
И если Оберон Вирта можно рассматривать в качестве минимального совре
менного языка последовательного программирования, то Оккам, созданный под
руководством Хоара, можно считать минимальным современным ЯПП. Этим он
для нас и интересен.
Как обычно в этой книге, представим модель Оккама (модель О), подчерки
вая ключевые концепции и принципы. Основной источник сведений – фирмен
ное руководство по программированию на ЯПП Оккам2 и сообщение о ЯПП
Оккам2 [31].
Поскольку параллелизмом мы уже занимались, перейдем сразу к особенно
стям ЯПП Оккам2. Сначала сформулируем эти особенности, а затем проиллюст
рируем их примерами.
1. Программа – это статически определенная иерархия процессов.
2. Процесс – это элементарный процесс или структура процессов. Допусти
мы, в частности, последовательные, параллельные, условные и иные струк
туры процессов.
3. В каждой структуре четко распределены роли как самих компонент, так
и средств их взаимодействия. Единый принцип этого распределения таков:
действия представлены процессами, память – переменными, обмен – кана
лами.
4. Общие переменные допустимы только у компонент некоторой последова
тельной структуры. (Общие константы допустимы и у компонент из раз
личных параллельных структур.)

Параллельное программирование в Оккаме/2 (модель О)
5. Канал Оккама – это простейший вариант асимметричного рандеву, допол
ненный усовершенствованными средствами связывания партнеров. Но
можно считать канал и посредником между партнерами по симметричному
рандеву. Такая двойственность зависит от того, рассматривается ли канал
с точки зрения одного процесса (асимметрия) или пары процессовпартне
ров (симметрия). Подчеркнем, что канал не обладает какойлибо памятью
(обмен через канал возможен только при взаимной готовности партнеров
процессов к такому обмену).
6. Каждый канал в программе обслуживает только одну пару партнеров по
обмену сообщениями. Связывание канала с парой партнеров происходит
статически. При этом один партнер определяется как источник сообщений,
а второй – как приемник. Партнерами по обмену могут быть только различ
ные компоненты некоторой параллельной (асинхронной) структуры про
цессов. Другими словами, партнерами по обмену могут быть только парал
лельные процессы.
7. С каждым каналом связывается тип передаваемых по нему сообщений (так
называемый протокол). Подразумевается квазистатический контроль со
гласованности сообщений с протоколом.
8. Коллективы (структуры) взаимодействующих процессов создаются стати
чески посредством так называемых «репликаторов» и коммутируются по
средством массивов каналов.
9. Связывание процессов с аппаратурой отделено от описания логики их взаи
модействия. Так что логическая функция программы не зависит от конфи
гурации (размещения процессов в процессорах).
355
5.2. Первые примеры применения
каналов
1. Буферпеременная.
byte лок: -- байтовая.переменная "лок".
seq -- последовательный комбинатор "seg"
источник ? лок -- получить из канала "источник" в "лок"
приемник ! лок -- передать из "лок" в канал "приемник"
2. Простейший фильтр – процесс (процедура) с двумя параметрамиканала
ми, протокол которых предусматривает передачу байтов.
рrос фильтр(chan of byte источник, приемник)
while true – бесконечный цикл
byte ëîê:
seq
источник ? лок
приемник ! лок.
рrос поставщик(chan of byte выход)
while true

356
byte X:
seq
выработать(X)
выход ! X
рrос потребитель(chan of byte вход)
while true
byte X:
seq
вход ? X
потребить(X)
Перспективы языков программирования
3. Можно теперь организовать из этих процессов параллельную структуру и
связать их напрямую через канал:
chan of byte связь: -- объявление байтового канала «связь»
par -- параллельный комбинатор «par»
поставщик(связь) -- компоненты такой структуры работают
потребитель(связь) -- параллельно (асинхронно)
А можно связать те же процессы и через буфер посредством двух каналов:
chan of byte связь 1, связь2:
par
поставщик(связь1)
фильтр(связь 1, связь2)
потребитель(связь2)
4. Можно организовать буфер любого нужного размера без особого труда.
val int N is 50: -- в буфере будет 50 ячеек
[N+l] chan of byte связь: -- массив из N каналов
par
поставщик(связь [0])
par i = 0 for N -- комбинатор с репликатором
фильтр(связь [i], связь[i +1] -- работает N фильтров
потребитель(связь[N]) -- (i îò 0 äî N–1)
Буфер составляется из N процессовфильтров, способных принять очеред
ной байт тогда и только тогда, когда предыдущий передан дальше. В ре
зультате в таком «живом» буфере каждый байт автоматически проталкива
ется вперед, если впереди есть место. И никаких забот о переполнении или
исчерпании буфера!
Итак, благодаря каналампосредникам с их встроенной синхронизациейобме
номбезочередей удается работать с процессами с полной абстракцией от их
асинхронной природы – они легко комбинируются в сложные структуры.
5.3. Сортировка конвейером фильтров
Интересно, что описываемая техника программирования асинхронных процессов
позволяет легко превратить цепь простейших фильтров (буфер) в цепь, сортиру
ющую поток из заданного количества чисел (например, по возрастанию).

Параллельное программирование в Оккаме/2 (модель О)
Идея состоит в том, чтобы каждый элемент цепи выделял минимальное из про
ходящих через него чисел (и затем посылал его вслед прошедшему потоку). Ясно,
что цепь из N таких элементов способна отсортировать последовательность из N
чисел.
Напишем элемент цепи – процесс фильтр.мин.
рrос фильтр.мин(chan of INT источник, приемник) =
INT ìèí, ñëåä:
seq
источник ? мин
seg i = 0 for N-l – комбинатор с репликатором
источник ? след
IF
ìèí <= ñëåä
приемник ! след
ìèí > ñëåä
seg
приемник ! мин
мин := след
приемник ! мин
Если заменить фильтр в предыдущей программе на фильтр.мин, получим тре
буемую сортировку. (Заметим, что каждый ее элемент пропускает через себя всю
последовательность чисел, но общее время сортировки линейно, так как она рабо
тает по конвейерному принципу.)
357
5.4. Параллельное преобразование
координат (умножение вектора
на матрицу)
При работе с графическими устройствами часто требуется выполнять линейные
преобразования nмерного пространства по формуле
у = Ах + b,
где А – квадратная матрица размера n*n, а у,х,b – nмерные векторы (х – исход
ный, b – постоянное смещение начала координат, у – результат преобразования).
Такие преобразования требуются, например, при отображении на экране пере
движения трехмерных объектов. Поскольку нужно преобразовывать координаты
каждой точки изображения, то в общем случае требуются сотни тысяч преобразо
ваний в секунду (если желательно создать эффект кинофильма или, по крайней
мере, не раздражать пользователя).
Будем рассматривать трехмерное пространство (n = 3), нумеруя координаты
с нуля (чтобы сразу учесть особенность индексации массивов в Оккаме). Анало
гичная задача рассмотрена в различных книгах по Оккаму, например в [29], но там Ок
кам старый.

358
Итак, наша программа должна вычислить координаты вектора у по формуле
(*) y[i] = SIGMA(a[i,j] * х[j]) + b[i],
где SIGMA – сумма по j от 0 до 2 (то есть n – 1).
Если основные затраты времени приходятся на умножения и сложения, а вре
мя дорого, то имеет смысл обратить внимание на возможность обеспечить распа
раллеливание всех нужных умножений (их ровно n**2 – в нашем случае 9) и не
которых сложений.
Если бы удалось поручить каждое умножение отдельному исполнителю (про
цессору), то можно было бы ускорить вычисления (в принципе) в n**2 раз! На
абстрактном уровне ЯПП исполнители представлены процессами, и при наличии
физических исполнителей компилятор позаботится сам о размещении различных
процессов на разных физических процессорах (или учтет явные пожелания про
граммиста). Во всяком случае, именно так работает компилятор Оккама2.
Однако, чтобы распределить умножения, требуется, вопервых, передать каж
дому процессу его аргументы; вовторых, уложиться в ограничения используемо
го ЯПП (в нашем случае главное из них – то, что канал может связывать точно два
процесса). Наконец, втретьих, нужно не только умножать, но и складывать, а так
же «поставлять» аргументы и «забирать» результаты.
Перспективы языков программирования
5.4.1. Структура коллектива процессов
Из формулы (*) видно, что каждому элементу a[i,j] матрицы А естественно сопос
тавить отдельный процесс pa[i,j], выполняющий умножение на этот элемент зна
чения х[j].
Если принять за основу эту идею (обеспечивающую требуемое ускорение
в n**2 раз), то остается вторая ключевая проблема – обеспечить подходящую ком
мутацию процессов pa[i,j]. Нужно ввести подходящие процессыпартнеры и свя
зать их подходящими каналами (по одному на пару партнеров!).
Понятно, что все pa [i,j] должны рано или поздно (лучше раньше) получить по
каналам значения x[j] и b[i]. Дисциплина «один канал на пару партнеров» требует
создать по процессу px[j] на каждый элемент вектора х и по процессу pb[i] на
каждый элемент вектора b[i].
С другой стороны, эта же дисциплина не позволяет передавать x[j] сразу не
скольким pa[i,j] по одному и тому же каналу. Естественно желать, чтобы число
каналов, связанных с процессом, было фиксировано (это упрощает и его пред
ставление на физическом уровне). Поэтому приходится применить сквозную пе
редачу данных через pa[i,j]. Этот характерный для Оккама элемент стиля
программирования можно назвать технологией фильтров. В нашем случае филь
трами служат pa[i,j].
Замечание (о фильтрах). В технологии фильтров каждый процесс рассматривает
ся как некоторый преобразователь потока данных (действие которого полностью
сводится к преобразованию потока). Название «фильтр» связано с тем, что в про
ходящем через преобразователь потоке подвергается обработке только вполне оп

Параллельное программирование в Оккаме/2 (модель О)
ределенный класс данных – остальные передаются в выходной поток без измене
ний. Обработанные фильтром (свои) данные заменяются в выходном потоке ре
зультатами обработки без нарушения естественного порядка в исходном потоке.
Технология фильтров помогает «нанизывать процессы на потоки данных» как бу
синки, собирая из относительно элементарных бусинок разнообразные програм
мысети. Полная аналогия с электрическими сетями, собираемыми из стандартных
элементов.
Важны отсутствие побочных эффектов и простота коммутации. Сравните с под
ключением процедуры, где нужно готовить аргументы, хранить результаты,
беспокоиться о глобальных ресурсах. Впрочем, аналогичная технология успешно
применяется и в последовательном программировании (на Фортране, Паскале,
Форте и др.) на основе процедурфильтров. Этот стиль используется в командном
языке ОС UNIX и в программировании на ЯП Си.
359
Наконец, следует заготовить процессы ру[i], получающие компоненты резуль
тирующего вектора y[i], а также процессы p0[j], назначение которых станет яс
ным чуть позже. Получается следующая схема (рис. 5.1):
px[0] рх[1] рх[2]
|| |
ру[0]<! ра[0,0] <! ра[0,1] <! ра[0,2] <! рb[0]
| | |
ру[1]<! ра[1,0] <! ра[1,1] <! ра[1,2] <! pb[l]
| | |
ру[2]<! ра[2,0] <! ра[2,1] <! ра[2,2] <! pb[2]
| | |
р0[0] р0[1] р0[2]
Рис. 5.1
Таким образом, конфигурация процессов разработана. Стрелки указывают на
правление потоков данных (им и должны соответствовать каналы).
Конечно, можно было бы для нашего случая описать каждый процесс (всего их
21) в отдельности. Однако это громоздко, и к тому же упустим главную цель –
показать Оккам2 в действии. Поэтому опишем процесс каждого вида процедурой
с параметрами (с тем чтобы затем воспользоваться средствами компоновки нуж
ной конфигурации из таких процедур).
Начнем с процесса вида ра. Такой процесс должен воспринимать «сверху» зна
чение x[j], справа – частичную сумму b[i] с «правыми» произведениями (до a[i,jl]
* х[j1] включительно) и передавать вниз без изменений значение х[j], а налево –
модифицированное на a[i,j] * x[j] значение частичной суммы. Параметрами про
цесса ра, следовательно, должны быть значения a[i,j] и четыре канала, два из кото
рых – для входных потоков, два – для выходных. Итак,
рrос pa(val real aij, chan real32 верх, низ, лево, право) =
eal xj,yi,aij.xj :
seg

360
âepx?xj
while true -- аналог loop
seg
par
íèç!xj -- передать xj
aij.xj := aij * xj
npaвo?yi -- запрос частичной суммы
par
ëåâî!ói + aij.xj
âepx?xj
Перспективы языков программирования
В результате повторений цикла можно обрабатывать при необходимости мно
го векторов (в зависимости от того, сколько поступит). Напишем параметриче
ский процесс pb. Он служит источником значений вектора b[i].
ðãîñ pb(val ãåàl32 bi, chan of real ëåâî) =
while true
ëåâî ! bi
Вопрос. Зачем здесь бесконечный цикл?
Подсказка. Это единственный способ обеспечить значениями работающие в анало
гичном цикле процессы pa[i,j].
Процесс рх поставляет значения вектора х[j].
prîñ ðõ(val real j, chan of real íèç) =
... -- создать x(j) каким-то способом в зависимости от j
while true
низ ! создать.x(j)
Процесс ру получает значения вектора y[i]
рrос ру(chan of real право) =
real êóäà.òî:
while true
...
право ? куда.то
...
Ясно, что два последних процесса – своего рода заглушки. Они обязательно
нужны, чтобы у внутренних процессов были партнеры.
Вопрос. Что произойдет при отсутствии или остановке таких партнеров?
Содержательно эти два процесса могут представлять собой, например, связь
с внешним миром, для описания которой в Оккаме имеются специальные сред
ства (например, так называемые порты).
Пора вспомнить о процессах р0. Они нужны только затем, чтобы в нижней
строке нашей структуры процессов могли стоять процессы вида ра (то есть с кана
лами «вниз»), хотя здесь уже вниз ничего передавать не надо. Процессы вида р0
также играют роль заглушек – они просто поглощают (содержательно лишние)
передачи вниз. Можно считать, что вместе с нижним рядом процессов вида ра они
должны образовать специальные процессы вида pal – без передачи вниз.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
