Параллельное программирование. Модели и приемы
Покупка
Основная коллекция
Тематика:
Программирование и алгоритмизация
Издательство:
СОЛОН-Пресс
Автор:
Федотов Илья Евгеньевич
Год издания: 2020
Кол-во страниц: 390
Дополнительно
Вид издания:
Практическое пособие
Уровень образования:
ВО - Бакалавриат
ISBN: 978-5-91359-222-4
Артикул: 447738.03.99
Книга посвящена рассмотрению некоторых высокоуровневых моделей параллельного и распределенного программирования. В порядке усложнения описываются несколько моделей внутренней организации параллельных программ: ярусно-параллельная форма программы, сети конечных автоматов, сети Петри, модель актеров, а также модель квантовых вычислений. Приводятся примеры программной реализации на C++ с использованием различных средств распараллеливания (OpenMP, MPI, POSIX Threads, Windows API). В каждом случае рассматриваются вопросы контекстно-независимой реализации конструкций описываемой модели без привязки к конкретным задачам, а также приведены примеры решения с использованием такой реализации некоторых конкретных задач. Некоторые из описанных моделей (к примеру, модель актеров), в настоящий момент приобретают все большую популярность вследствие распространения основанных на ее использовании языков и библиотек. Книга ориентирована на подготовленного читателя в области программирования. Будет полезна программистам, желающим освоить высокоуровневые подходы к организации параллельных и распределенных программ, студентам старших курсов, аспирантам и преподавателям технических ВУЗов, преподающим параллельное программирование.
Тематика:
ББК:
УДК:
- 004: Информационные технологии. Вычислительная техника...
- 681: Точная механика. Автоматика. Приборостроение
ОКСО:
- ВО - Бакалавриат
- 02.03.01: Математика и компьютерные науки
- 02.03.02: Фундаментальная информатика и информационные технологии
- 09.03.01: Информатика и вычислительная техника
- 09.03.02: Информационные системы и технологии
- 09.03.03: Прикладная информатика
- ВО - Магистратура
- 02.04.01: Математика и компьютерные науки
- 02.04.02: Фундаментальная информатика и информационные технологии
- 09.04.01: Информатика и вычислительная техника
- 09.04.02: Информационные системы и технологии
- 09.04.03: Прикладная информатика
ГРНТИ:
Скопировать запись
Фрагмент текстового слоя документа размещен для индексирующих роботов
Серия «Библиотека профессионала» И. Е. Федотов Параллельное программирование Модели и приемы СОЛОН-Пресс Москва 2018 2020
УДК 681.3 ББК 32.97 Ф 34 Федотов И. Е. Параллельное программирование. Модели и приемы. — М.: СОЛОН-Пресс, 2018. — 390 с. (Серия «Библиотека профессионала») ISBN 978-5-91359-222-4 Книга посвящена рассмотрению некоторых высокоуровневых моделей параллельного и распределенного программирования. В порядке усложнения описываются несколько моделей внутренней организации параллельных программ: ярусно-параллельная форма программы, сети конечных автоматов, сети Петри, модель актеров, а также модель квантовых вычислений. Приводятся примеры программной реализации на C++ с использованием различных средств распараллеливания (OpenMP, MPI, POSIX Threads, Windows API). В каждом случае рассматриваются вопросы контекстно-независимой реализации конструкций описываемой модели без привязки к конкретным задачам, а также приведены примеры решения с использованием такой реализации некоторых конкретных задач. Некоторые из описанных моделей (к примеру, модель актеров), в настоящий момент приобретают все большую популярность вследствие распространения основанных на ее использовании языков и библиотек. Книга ориентирована на подготовленного читателя в области программирования. Будет полезна программистам, желающим освоить высокоуровневые подходы к организации параллельных и распределенных программ, студентам старших курсов, аспирантам и преподавателям технических ВУЗов, преподающим параллельное программирование. Сайт журнала «Ремонт & Сервис»: www.remserv.ru Сайт издательства «СОЛОН-Пресс»: www.solon-press.ru ISBN 978-5-91359-222-4 © «СОЛОН-Пресс», 2018 © Федотов И. Е., 2018 КНИГА — ПОЧТОЙ Книги издательства «СОЛОН-Пресс» можно заказать наложенным платежом (оплата при получении) по фиксированной цене. Заказ можно оформить одним из трех способов: 1. Послать открытку или письмо по адресу: 123001, Москва, а/я 82. 2. Оформить заказ на сайте www.solon-press.ru в разделе «Книга — почтой». 3. Заказать книгу по тел. (495) 617-39-64, (495) 617-39-65. При оформлении заказа следует правильно и полностью указать адрес, по которому должны быть высланы книги, а также фамилию, имя и отчество получателя. Желательно указать дополнительно свой телефон и адрес электронной почты. Через Интернет вы можете в любое время получить свежий каталог издательства «СОЛОНПресс», считав его с адреса http://www.solon-press.ru/katalog. Интернет-магазин размещен на сайте www.solon-press.ru. По вопросам приобретения книг обращаться: ООО «СОЛОН-Пресс» Тел: (495) 617-39-64, (495) 617-39-65, E-mail: kniga@solon-press.ru, www.solon-press.ru 2020 2020 2020
Оглавление Предисловие 6 О проблеме параллельного программирования . . . . . . . . . . . . . . . . . . . . . 6 О целях издания . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 О содержании . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Об используемой терминологии . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Некоторые вопросы стиля . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1. Программные интерфейсы 22 1.1. Интерфейс OpenMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 1.1.1. Беглый взгляд «под капот» OpenMP . . . . . . . . . . . . . . . . . . . . 23 1.1.2. Основные конструкции параллельного выполнения . . . . . . . . . . . 29 1.1.3. Некоторые вспомогательные директивы . . . . . . . . . . . . . . . . . . 34 1.1.4. Разделение данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 1.1.5. Runtime-функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 1.1.6. Вычисление определенного интеграла . . . . . . . . . . . . . . . . . . . 44 1.2. Интерфейс передачи сообщений MPI . . . . . . . . . . . . . . . . . . . . . . . . 47 1.2.1. Снова ряд Лейбница . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 1.2.2. Краткое описание предоставляемых функций . . . . . . . . . . . . . . . 55 1.2.3. Распределение вычислений в однородной среде . . . . . . . . . . . . . . 65 1.2.4. Некоторые вопросы распределения в неоднородной среде . . . . . . . . 72 1.2.5. Умножение матрицы на вектор . . . . . . . . . . . . . . . . . . . . . . . 75 1.2.6. Перемножение матриц . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 2. Ярусно-параллельная форма программы 91 2.1. Цель и механизм построения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 2.2. Варианты реализации механизма . . . . . . . . . . . . . . . . . . . . . . . . . . 95 2.2.1. Поярусное выполнение комплекса работ . . . . . . . . . . . . . . . . . . 95 2.2.2. Учет индивидуальных зависимостей работ . . . . . . . . . . . . . . . . 98 2.3. Симуляция выполнения логических схем . . . . . . . . . . . . . . . . . . . . . . 103 3. Сети конечных автоматов 111 3.1. Программирование конечных автоматов . . . . . . . . . . . . . . . . . . . . . . 111 3.2. Параллелизм сетей конечных автоматов . . . . . . . . . . . . . . . . . . . . . . 115 3.3. Пример программной реализации . . . . . . . . . . . . . . . . . . . . . . . . . . 116 3.3.1. Реализация с использованием OpenMP . . . . . . . . . . . . . . . . . . . 118 3.3.2. Простая реализация с использованием MPI . . . . . . . . . . . . . . . . 123 3.3.3. Реализация с поддержкой вложенных сетей . . . . . . . . . . . . . . . . 125 3.4. Примеры сетей автоматов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 3.4.1. Параллельный сумматор . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 3
ОГЛАВЛЕНИЕ 3.4.2. Прямоугольный бильярд . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 4. Сети Петри 147 4.1. Краткое введение в теорию сетей Петри . . . . . . . . . . . . . . . . . . . . . . 147 4.1.1. Знакомство с сетями Петри . . . . . . . . . . . . . . . . . . . . . . . . . 147 4.1.2. Строго иерархические сети . . . . . . . . . . . . . . . . . . . . . . . . . . 151 4.1.3. Параллельные вычисления и синхронизация . . . . . . . . . . . . . . . 152 4.1.4. Задача об обедающих философах . . . . . . . . . . . . . . . . . . . . . . 155 4.1.5. Задача чтения-записи . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 4.2. Программная реализация . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 4.2.1. Функционирование строго иерархических сетей . . . . . . . . . . . . . . 163 4.2.2. Выполнение параллельных процессов . . . . . . . . . . . . . . . . . . . 168 4.3. Некоторые примеры использования . . . . . . . . . . . . . . . . . . . . . . . . . 179 4.3.1. Реализация игры в жанре «квест» . . . . . . . . . . . . . . . . . . . . . 179 4.3.2. Обработка потоков данных . . . . . . . . . . . . . . . . . . . . . . . . . 188 4.3.3. Реализация задачи об обедающих философах . . . . . . . . . . . . . . . 192 5. Модель актеров 197 5.1. Описание модели актеров . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 5.1.1. Первоначальное описание модели . . . . . . . . . . . . . . . . . . . . . . 197 5.1.2. Язык SAL для описания поведения актеров . . . . . . . . . . . . . . . . 201 5.1.3. Некоторые существующие модификации модели . . . . . . . . . . . . . 205 5.2. Различные варианты реализации . . . . . . . . . . . . . . . . . . . . . . . . . . 208 5.2.1. Простая одноуровневая реализация . . . . . . . . . . . . . . . . . . . . . 208 5.2.2. Многопроцессный вариант . . . . . . . . . . . . . . . . . . . . . . . . . . 219 5.2.3. Низкоуровневая многопоточная реализация . . . . . . . . . . . . . . . . 230 5.2.4. Поддержка вложенных подсистем актеров . . . . . . . . . . . . . . . . . 239 5.3. Примеры решения некоторых задач . . . . . . . . . . . . . . . . . . . . . . . . 247 5.3.1. Вычисление факториала . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 5.3.2. Числа Фибоначчи . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 5.3.3. Задача чтения-записи . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262 5.3.4. Вычисление количества максимальных значений . . . . . . . . . . . . . 268 5.3.5. Поиск выхода из лабиринта . . . . . . . . . . . . . . . . . . . . . . . . . 272 6. Квантовые вычисления 280 6.1. Описание вычислительной модели . . . . . . . . . . . . . . . . . . . . . . . . . 280 6.1.1. Классические обратимые вычисления . . . . . . . . . . . . . . . . . . . 281 6.1.2. Квантовый бит и принцип суперпозиции . . . . . . . . . . . . . . . . . . 285 6.1.3. Системы кубитов и квантовая запутанность . . . . . . . . . . . . . . . . 289 6.1.4. Унитарные преобразования и квантовые схемы . . . . . . . . . . . . . . 291 6.1.5. Измерение результата вычислений . . . . . . . . . . . . . . . . . . . . . 294 6.1.6. Параллелизм в квантовых вычислениях . . . . . . . . . . . . . . . . . . 296 6.2. Симулятор квантового компьютера . . . . . . . . . . . . . . . . . . . . . . . . . 298 6.2.1. Виртуальный квантовый вычислитель . . . . . . . . . . . . . . . . . . . 298 6.2.2. Реализация базовых вентилей . . . . . . . . . . . . . . . . . . . . . . . . 302 6.3. Алгоритм Дойча . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 6.4. Aлгоритм Гровера . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307 6.4.1. Общее описание . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 6.4.2. Схема обращения знака . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 6.4.3. Обращение относительно среднего . . . . . . . . . . . . . . . . . . . . . 310
ОГЛАВЛЕНИЕ 5 6.4.4. Полная схема алгоритма . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 6.5. Полная реализация алгоритма Шора . . . . . . . . . . . . . . . . . . . . . . . . 314 6.5.1. Общая схема и описание . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 6.5.2. Модульное возведение в степень . . . . . . . . . . . . . . . . . . . . . . . 320 6.5.3. Квантовое преобразование Фурье . . . . . . . . . . . . . . . . . . . . . . 337 6.5.4. Извлечение порядка из результата измерения . . . . . . . . . . . . . . . 338 А. Шаблоны классов матрицы и вектора 341 Б. Классы для выполнения комплексов работ 345 В. Классы для выполнения сетей конечных автоматов 349 Г. Классы для выполнения сетей Петри 357 Д. Классы для выполнения систем актеров 365 Е. Классы для симуляции квантовых вычислений 377 Литература 385
Предисловие Сравнительно недавно современные компьютеры вплотную приблизились к своему пределу тактовых частот. Производительность процессоров (точнее, их отдельных ядер) перестала нарастать сказочными темпами, и теперь остается возможность обеспечить дальнейшее повышение вычислительных мощностей лишь за счет увеличения их количества: производительность компьютеров начала расти «вширь», а не «в высоту». В связи с этим параллельное программирование уже перестало быть узкоспециализированной дисциплиной для высокопроизводительных вычислений и приобрело большую актуальность для широких масс представителей программистского сообщества. Есть немало публикаций, посвященных рассмотрению архитектур параллельных вычислительных систем и теоретическим вопросам распараллеливания последовательных алгоритмов. Также существует достаточно учебных пособий и статей, посвященных рассмотрению существующих технологий, программных интерфейсов и библиотек. В то же время, довольно давно создано немало моделей, успешно описывающих выполнение параллельных процессов, которые уже являются основой многих программных технологий и некоторых не слишком популярных инструментов построения параллельных программ. Таким моделям посвящено немало учебного материала теоретического характера, однако наблюдается некоторый дефицит в области практического их рассмотрения с доступными примерами программной реализации на основе современных средств. В результате освещения таких моделей лишь с теоретической точки зрения они, порой, остаются вне практического арсенала программиста, отчего возможность использования их при решении конкретной задачи зачастую даже не рассматривается. Настоящее издание стремится в какой-то степени восполнить указанную нишу и исправить такое положение. О проблеме параллельного программирования Главная проблема параллельного программирования основана на относительной сложности построения схемы параллельных вычислений в голове программиста. Человек в принципе мыслит последовательно. Здесь, разумеется, имеется в виду процесс построения умозаключений, а не внутренние процессы мозга на физиологическом уровне. Именно поэтому последовательны большинство существующих сегодня языков программирования. Многие авторы, работающие в области параллельного программирования, сходятся во мнении, что развитой дисциплины параллельного программирования на сегодняшний момент нет, и что индустрия создания параллельных программ движется по тупиковому пути [21, 25, 24, 69]. Большинство сегодняшних параллельных вычислений реализуется в виде параллельнопоследовательных программ. При этом в существующем последовательном алгоритме выделяются независимые друг от друга последовательные ветви, которые могут быть выполнены параллельно, и с этим учетом пишется параллельно-последовательная программа. Нередко берется уже существующая последовательная программа, которая путем добавления неких конструкций распараллеливания преобразуется в параллельно-последовательную. 6
О ПРОБЛЕМЕ ПАРАЛЛЕЛЬНОГО ПРОГРАММИРОВАНИЯ 7 Чем сложнее исходная последовательная программа, чем хуже она структурирована, чем запутаннее зависимости между отдельными ее ветвями, тем сложнее оказывается выполнить ее распараллеливание. Случаются ситуации, когда существующая последовательная программа реализует некоторый алгоритм, в котором изначально присутствует логический параллелизм, однако реализация этого алгоритма в программе обременена сложным взаимодействием участков кода, которые в идеале могут и должны быть независимыми. В таком случае бывает сложно без существенных изменений программы выделить в ней ветви, выполнение которых может быть физически распараллелено. Использование моделей параллельного программирования, в том числе рассматриваемых в настоящем издании, обеспечивает основу для такого описания задач, при котором присущий им логический параллелизм выявляется изначально и не теряется в дальнейшем. Существует немало изданий, посвященных интерфейсам и технологиям параллельного программирования, наподобие рассматриваемых ниже MPI и OpenMP. Однако это лишь инструменты, а одного прекрасного знания инструмента, как правило, недостаточно для того, чтобы легко и эффективно реализовать решение конкретной задачи. Помимо знаний о том, какой инструмент нужно использовать и каковы правила его использования (непосредственные требования спецификации), необходимы знания о том, какие цели должны быть при этом достигнуты и, самое главное, каким образом инструмент должен помочь в достижении этих целей (идиоматические конструкции, образцы (patterns) решения типовых задач и т.п.). Именно такую роль в параллельном программировании выполняют соответствующие вычислительные модели. Многие авторы публикаций по параллельным вычислениям сходятся в том, что для описания параллельных программ необходимо уйти от императивных языков программирования [10, 12]. Императивные языки (от лат. imperativus — повелительный) описывают, какие действия и в каком порядке следует выполнить, чтобы получить результат. Такими является большинство популярных сегодня языков: C++, Pascal, Java, Perl и т.п. В данном же случае требуется использование декларативных языков (от лат. declaratio — заявление, объявление), т.е. таких, которые описывают, чем является результат, который должен быть получен, оставив выполнение действий, как и выбор их последовательности, на долю компилятора или интерпретатора. Примерами декларативных языков являются язык логического программирования Prolog и языки функционального программирования Haskell, Erlang, Lisp и т.п. Различие между императивным и декларативным программированием в чем-то напоминает различие между прямым вычислением функции и решением уравнения, т.е. обращением функции. С момента становления программирования как дисциплины декларативным языкам пророчили большое будущее, поскольку они зачастую позволяют легко, наглядно и потому надежно описать задачу, что само по себе обычно гораздо проще, чем детально описывать процесс ее решения. При этом возникает другая сложность: создание эффективного компилятора или интерпретатора, который, в общем-то, и принимает в этом случае бремя принятия решений о выполнении действий, снятое с плеч программиста. Несмотря на потенциальную мощь, декларативные языки не приобрели широкой популярности. Одна из возможных причин заключается в том, что на момент их появления мощности машин не хватало для реализации достаточно эффективных компиляторов и интерпретаторов. Вследствие этого программисты были вынуждены пользоваться языками, которые позволяют «подсказать», а вернее даже «повелеть» машине выполнение конкретных действий, т.е. императивными языками. Ситуация здесь сродни той, почему в прежние времена было популярно использование низкоуровневых языков. Оптимизирующие компиляторы языков высокого уровня и вы
ПРЕДИСЛОВИЕ числительные системы, на которых они работали, не были достаточно мощными, чтобы обеспечить производительность и размер программ, сравнимые с программами на языках низкого уровня. Именно это является одной из причин, по которым Д. Кнут избрал машинно-ориентированный язык для представления алгоритмов в своем известном многотомнике, о чем он и говорит в самом начале первого тома. Когда оптимизаторы кода достигли приемлемого уровня качества, программирование на языках низкого уровня постепенно утратило популярность в угоду повышению переносимости. Однако переход на языки высокого уровня не потребовал смены парадигмы, и языки остались императивными. Смена языка в рамках одной парадигмы гораздо проще смены парадигмы, к тому же для перехода на языки высокого уровня был стимул: повышение переносимости. Развитие вычислительных мощностей сказалось также и на повышении эффективности использования компиляторов и интерпретаторов декларативных языков. Более того, в отношении смены парадигмы на декларативную теперь также появился стимул: перенос реализации механизмов распараллеливания с программистов на компиляторы и интерпретаторы. Такие механизмы являются частью системы, и забота о них не должна лежать на плечах прикладного программиста. Поэтому можно надеяться, что в скором времени изменится и отношение к декларативному программированию. Есть мнение, что вместо освоения новых языков программирования необходимо пополнение конструкциями распараллеливания традиционных последовательных языков (императивных), поскольку первое гораздо дороже обходится с точки зрения переобучения персонала. Подобные аргументы допустимы лишь в рамках корпоративной политики отдельно взятых производств, в общем же случае они попросту встают на пути прогресса. Нельзя не признать, однако, что какой-то переходный этап на пути от императивных языков к декларативным неизбежен. Существует и другое мнение, утверждающее, что выбор императивных языков был изначально неверным, поскольку вынуждает задавать в программе больше деталей, чем необходимо для представления алгоритма, тем самым усложняя процесс описания решаемой задачи программистом компьютеру. Задание четкой последовательности действий в ситуации, когда она не важна, — одна из таких «ненужных» подробностей, присущая императивным языкам. Многие могут возразить: зачем использовать декларативные языки, если машина все равно выполняет императивный поток инструкций? С тем же успехом можно спросить, зачем вообще использовать языки высокого уровня, если можно программировать сразу в машинных инструкциях. Машина «мыслит» совершенно не так, как человек, и здесь не возникает разногласий. Человеку нелегко дается описывать задачу в терминах машины. Высокоуровневое же программирование призвано быть как раз промежуточным слоем, обеспечивающим преобразование описания задачи, представленного в терминах человека (на формальном, но все же приближенном к представлению человека языке), в описание той же задачи, представленное в терминах машины. Чем проще, естественнее и точнее высокоуровневый язык позволяет человеку описать свою задачу, тем лучше он соответствует своему назначению. В этом отношении декларативные языки, определенно, во многих областях опережают императивные. Многие существующие уже давно языки декларативного программирования, несмотря на свою мощь, не обеспечивают полноценной базовой платформы для удобного описания программы с возможностью неявной передачи присущей ей внутренней параллельной структуры. Причина, вероятно, в том, что спецификации этих языков создавались тогда, когда вопрос параллельного программирования не стоял так остро, как сегодня, и, видимо, поэтому в них зачастую содержатся ограничения, также сводящие вычисления к последовательным. К примеру, наличие в языке Lisp операции присваивания вносит необходимость задания четкого порядка для вычисления последовательности выражений (к примеру, ар
О ЦЕЛЯХ ИЗДАНИЯ 9 гументов функции), что лишает интерпретатор права распараллелить этот процесс, даже если программисту этот порядок не важен [10]. Выходит, для полноценного удобного описания параллельных программ, так или иначе, требуется более современный язык. Возможно, им станет один из уже существующих языков, попыток создания которых, в том числе весьма удачных, на сегодняшний момент известно немало. Среди них, к примеру, мультипарадигменный язык Oz. Как и в случае многих других языков, нельзя сказать, что параллельное программирование является первостепенной его задачей, однако удобство и эффективность, с которыми оно выполняется, является естественным следствием выразительности соответствующих декларативных языковых конструкций. Параллельное выполнение в Oz основано на легковесных потоках управления (green threads) и элементах модели потоков данных (dataflow), близкой к рассматриваемому ниже ярусно-параллельному представлению программы. Функциональный язык Erlang изначально создавался с целью построения распределенных систем, в связи с чем предоставляет не менее удобные конструкции для создания параллельных программ, а также высокую степень эффективности их выполнения. Модель существования и взаимодействия параллельно выполняющихся процессов в Erlang близка к модели актеров, которая также будет рассмотрена ниже. Многие языки, такие как, к примеру, Alice, используют понятие фьючеров (future, будущее значение), которые напоминают переменные и представляют собой результат выполнения каких-либо еще не завершенных операций, выполняемых обычно параллельно. Наконец, существуют языки, которые сами по себе не предоставляют явно конструкций для распараллеливания, однако мощь и выразительность их синтаксиса позволяет создание для этих целей специальных библиотек, удобство и прозрачность использования которых оказывается сравнима с использованием языковых конструкций. Таким является мультипарадигменный язык Scala, компилируемый в байт-код для виртуальной машины Java. Эти и другие подобные языки, несмотря не свои достоинства, пока не обрели достаточно широкой популярности, и можно лишь надеяться, что они обретут ее в будущем. В противном случае можно надеяться, что появятся и обретут широкую популярность другие высокоуровневые языки для описания параллельных программ. Пока же мы будем отталкиваться от факта их отсутствия, в связи с чем будем рассматривать популярные модели параллельных вычислений с примерами на основе популярного императивного языка. Следует понимать, что под прозвучавшим утверждением об отсутствии популярных языков параллельного программирования подразумевалось отсутствие таких языков, которые не ориентированы на какую-либо узкоспециализированную область применения и которые используются для реализации параллельных вычислений повсеместно. Стоит отметить, что популярность средства программирования очень важна в сегодняшних условиях, когда большинство программистов вынуждены быть «совместимыми» друг с другом. Временные затраты на освоение многочисленных, хоть и прогрессивных, но зачастую мало распространенных технологий, как правило, не оправдывают себя, вследствие чего большинство программистов вынуждены консервативно пользоваться решениями на основе, порой, не самых удачных, но устоявшихся средств. О целях издания Представленное в книге описание моделей параллельного программирования, помимо очевидных академических целей, стремится проиллюстрировать следующие два взаимосвязанных момента. Во-первых, ставится задача показать проблему современного параллельного программирования, заключающуюся, прежде всего, в том, что наиболее популярные сегодня средства
ПРЕДИСЛОВИЕ программирования приспособлены к описанию методов решения стоящей задачи в соответствии с образом мышления программиста, а именно в последовательной форме. Отсюда вытекает сложность наглядного для человека представления с использованием таких средств параллельных алгоритмов и программ. В связи с этим встает ребром вопрос необходимости использования для параллельного программирования принципиально иных средств, не являющихся модификациями или надстройками существующих популярных языков. Одним из возможных направлений является декларативное программирование, что косвенно и демонстрируется примерами в книге, поскольку большинство приведенных программ, хоть и реализованы на императивном языке, основаны на декларативном представлении задачи. Во-вторых, делается попытка проиллюстрировать следующий факт, вытекающий из рассмотрения тех же обстоятельств с обратной стороны. Несмотря на указанные сложности, императивные языки, все же, могут без серьезных препятствий быть использованы для прозрачной (неявной) реализации параллельных алгоритмов. При этом может быть реализована некая абстрактная машина, принимающая на входе набор декларативно описывающих алгоритм данных, которая инкапсулирует внутри себя механизмы параллельного выполнения. Программный интерфейс такой машины принципиально не зависит от средств распараллеливания, с помощью которых она реализована и которые в общем случае могут быть разными. Такой подход позволит не уходить от имеющейся на сегодняшний момент ситуации преобладания императивных языков и, тем самым, в какой-то степени упростит и сгладит освоение параллельного программирования. Оба этих момента взаимно дополняют друг друга. Учитывая отсутствие на сегодняшний момент развитых и одновременно обладающих широкой популярностью средств декларативного параллельного программирования, в практических целях может быть рассмотрен подход с использованием механизма обработки декларативных данных, реализованного на императивном языке. Однако в целом такие меры являются вынужденными и требуют дополнительных затрат, вследствие чего в будущем требуется появление новых либо популяризация существующих более мощных средств разработки, изначально ориентированных на параллельное программирование и не являющихся видоизменением каких-либо средств последовательного программирования. Разумеется, ни одна из представленных в настоящем издании моделей не является панацеей и не дает возможности простого описания для любой задачи, вопреки убеждениям некоторых их приверженцев. Одни задачи наиболее удобно описываются одной моделью вычислений, другие — другой, а третьи наиболее просто и наглядно описываются простым циклом на императивном языке. Тем не менее, понимание рассмотренных моделей и парадигм в комплексе может существенно упростить разработку параллельных программ с той точки зрения, что даст возможность программисту смотреть шире и сгладит отсутствие у него «параллельного мышления». Готовые примеры программ «со стороны», наподобие приведенных здесь, практически никогда не оказываются подходящими во всех отношениях, однако это лишь добавляет стимула для более глубокого самостоятельного изучения соответствующей темы. О содержании Факт наличия параллелизма среды выполнения порождает необходимость распараллеливания ресурсоемких вычислений, однако сама по себе эта необходимость в общем случае не привязана к конкретной реализации параллельной среды. В связи с этим в книге не приводится классификация параллельных вычислительных систем и не делается акцента на конкретную категорию системы. Для ознакомления с классификацией можно обратиться к другой литературе [1, 4, 9, 15].