Введение
Система передачи сообщений для параллельных компьютеров
Message Passing Interface (MPI) – это стандартизированный и переносимый стандарт передачи сообщений, разработанный для работы на архитектурах параллельных вычислений. Стандарт MPI определяет синтаксис и семантику библиотечных функций, полезных для широкого круга пользователей, разрабатывающих переносимые программы обмена сообщениями на языках C, C++ и Fortran. Существует несколько реализаций MPI с открытым исходным кодом, которые способствовали развитию индустрии параллельного программного обеспечения и стимулировали разработку переносимых и масштабируемых параллельных приложений большого масштаба.
История
Усилия по созданию интерфейса передачи сообщений начались летом 1991 года, когда небольшая группа исследователей начала обсуждение на горном курорте в Австрии. В результате этого обсуждения 29–30 апреля 1992 года в Вильямсбурге (штат Вирджиния) был проведен семинар по стандартам передачи сообщений в распределенной памяти. Участники семинара в Вильямсбурге обсудили основные характеристики, необходимые для стандартного интерфейса передачи сообщений, и сформировали рабочую группу для продолжения процесса стандартизации. Джек Донгарра, Тони Хей и Дэвид У. Уокер представили предварительный проект "MPI1" в ноябре 1992 года. В ноябре 1992 года в Миннеаполисе состоялось заседание рабочей группы MPI, на котором было решено придать процессу стандартизации более формальный характер. Рабочая группа MPI встречалась каждые 6 недель в течение первых 9 месяцев 1993 года. Проект стандарта MPI был представлен на конференции Supercomputing '93 в ноябре 1993 года. После периода публичных комментариев, в результате которых были внесены некоторые изменения в MPI, версия 1.0 MPI была выпущена в июне 1994 года. Эти встречи и переписка по электронной почте вместе составили MPI Forum, членство в котором было открыто для всех участников сообщества высокопроизводительных вычислений. В разработке MPI приняли участие около 80 человек из 40 организаций, преимущественно из США и Европы. Большинство крупных производителей параллельных компьютеров участвовали в работе над MPI, сотрудничая с исследователями из университетов, государственных лабораторий и промышленности. MPI предоставляет производителям параллельного оборудования четко определенный базовый набор функций, которые могут быть эффективно реализованы. В результате производители оборудования могут использовать эту коллекцию стандартных низкоуровневых функций для создания функций более высокого уровня для среды связи распределенной памяти, поставляемой с их параллельными машинами. MPI обеспечивает простой в использовании и переносимый интерфейс для обычного пользователя, но при этом достаточно мощный, чтобы позволить программистам использовать высокопроизводительные операции передачи сообщений, доступные на современных машинах. В стремлении создать универсальный стандарт передачи сообщений, исследователи не основывались на какой-либо одной системе, а включили в него наиболее полезные функции нескольких систем, включая системы, разработанные компаниями IBM, Intel, nCUBE, PVM, Express, P4 и PARMACS. Парадигма передачи сообщений привлекательна благодаря широкой переносимости и может использоваться для связи в системах с распределенной и общей памятью, в сетях рабочих станций и в комбинации этих элементов. Эта парадигма может применяться в различных условиях, независимо от скорости сети или архитектуры памяти. Поддержка встреч MPI была частично обеспечена DARPA и Национальным научным фондом США (NSF) в рамках гранта ASC 9310330, соглашения о сотрудничестве NSF Science and Technology Center № CCR 8809615, а также Европейской комиссией через проект Esprit P6643. Университет Теннесси также оказал финансовую поддержку MPI Forum.
Обзор
MPI — это протокол связи для программирования параллельных компьютеров. Поддерживаются как коммуникация типа «точка-точка», так и коллективная коммуникация. MPI — «это интерфейс программирования приложений для передачи сообщений, а также спецификации протокола и семантики, определяющие поведение его функций в любой реализации». Целями MPI являются высокая производительность, масштабируемость и переносимость. MPI остаётся доминирующей моделью, используемой в высокопроизводительных вычислениях на сегодняшний день. MPI не утверждён каким-либо крупным органом по стандартизации; тем не менее, он стал де-факто стандартом для связи между процессами, моделирующими параллельную программу, работающую в распределённой системе памяти. Фактические распределённые суперкомпьютеры, такие как компьютерные кластеры, часто запускают такие программы. Основная модель MPI 1 не предполагает концепции общей памяти, а MPI 2 имеет лишь ограниченную концепцию распределённой общей памяти. Тем не менее, программы MPI регулярно запускаются на компьютерах с общей памятью, и MPICH и Open MPI могут использовать общую память для передачи сообщений, если она доступна. Проектирование программ на основе модели MPI (в отличие от моделей с явной общей памятью) имеет преимущества при работе на архитектурах NUMA, поскольку MPI способствует локальности памяти. Явное программирование с использованием общей памяти было введено в MPI 3. Хотя MPI относится к 5-му и более высоким уровням эталонной модели OSI, реализации могут охватывать большинство уровней, при этом сокеты и протокол управления передачей (TCP) используются на транспортном уровне. Большинство реализаций MPI состоят из определённого набора подпрограмм, которые можно непосредственно вызывать из C, C++, Fortran (то есть API) и любого языка, способного взаимодействовать с такими библиотеками, включая C#, Java или Python. Преимущества MPI по сравнению со старыми библиотеками передачи сообщений заключаются в переносимости (поскольку MPI был реализован практически для каждой архитектуры распределённой памяти) и скорости (поскольку каждая реализация, по сути, оптимизирована для аппаратного обеспечения, на котором она работает). MPI использует независимые от языка спецификации (LIS) для вызовов и языковых привязок. В первом стандарте MPI были определены привязки ANSI C и Fortran 77 вместе с LIS. Проект был представлен на конференции Supercomputing 1994 (ноябрь 1994 г.) и вскоре после этого был окончательно утверждён. Около 128 функций составляют стандарт MPI 1.3, который был выпущен как завершение серии MPI 1 в 2008 году. В настоящее время стандарт имеет несколько версий: версия 1.3 (обычно обозначаемая как MPI 1), которая делает акцент на передаче сообщений и имеет статическую среду выполнения, MPI 2.2 (MPI 2), которая включает в себя новые функции, такие как параллеский ввод-вывод, динамическое управление процессами и операции удалённой памяти, и MPI 3.1 (MPI 3), которая включает в себя расширения коллективных операций с неблокирующими версиями и расширения операций одностороннего доступа. LIS MPI 2 определяет более 500 функций и предоставляет языковые привязки для ISO C, ISO C++ и Fortran 90. Также была добавлена объектная совместимость для упрощения программирования с использованием смешанных языков передачи сообщений. Побочным эффектом стандартизации MPI 2, завершённой в 1996 году, стало уточнение стандарта MPI 1, что привело к созданию MPI 1.2. MPI 2 в основном является надмножеством MPI 1, хотя некоторые функции устарели. Программы MPI 1.3 по-прежнему работают в реализациях MPI, соответствующих стандарту MPI 2. MPI 3 включает в себя новые привязки Fortran 2008, в то время как устаревшие привязки C++ и многие устаревшие подпрограммы и объекты MPI были удалены. MPI часто сравнивают с Parallel Virtual Machine (PVM) — популярной распределённой средой и системой передачи сообщений, разработанной в 1989 году, которая была одной из систем, стимулировавших потребность в стандартной параллельной передаче сообщений. Модели программирования с потоковой общей памятью (такие как Pthreads и OpenMP) и программирование с передачей сообщений (MPI/PVM) можно рассматривать как взаимодополняющие и иногда используются вместе, например, на серверах с несколькими крупными узлами общей памяти.
Функциональность
Интерфейс MPI предназначен для обеспечения базовой виртуальной топологии, синхронизации и функциональности обмена данными между набором процессов (которые сопоставлены с узлами/серверами/компьютерными экземплярами) независимо от языка программирования, с использованием синтаксиса (привязок), специфичного для языка, а также некоторых дополнительных функций, специфичных для языка. Программы MPI всегда работают с процессами, но программисты часто называют процессы процессорами. Как правило, для достижения максимальной производительности каждому процессору (или ядру в многоядерной машине) назначается только один процесс. Это назначение происходит во время выполнения посредством агента, запускающего программу MPI, обычно называемого mpirun или mpiexec. Функции библиотеки MPI включают, помимо прочего, операции отправки/приема типа "точка-точка", выбор между декартовой или графоподобной логической топологией процессов, обмен данными между парами процессов (операции отправки/приема), объединение частичных результатов вычислений (операции сбора и сведения), синхронизацию узлов (операция барьера), а также получение информации о сети, такой как количество процессов в вычислительной сессии, идентификатор текущего процессора, к которому сопоставлен процесс, соседние процессы, доступные в логической топологии, и т.д. Операции "точка-точка" могут быть синхронными, асинхронными, буферизованными и готовыми, что позволяет использовать как более строгую, так и более слабую семантику для аспектов синхронизации при обмене данными методом rendezvous. Во многих реализациях возможно выполнение множества операций в асинхронном режиме. MPI 1 и MPI 2 допускают реализации, которые позволяют перекрывать операции обмена данными и вычисления, однако практика и теория в этом вопросе различаются. MPI также определяет потокобезопасные интерфейсы, которые обладают связностью и стратегиями связи, помогающими избежать скрытого состояния в интерфейсе. Относительно легко писать многопоточные программы MPI, использующие операции "точка-точка", и некоторые реализации поддерживают такой код. Коллективная многопоточная коммуникация лучше всего реализуется с использованием нескольких копий коммуникаторов, как описано ниже.
Понятия
MPI предоставляет ряд возможностей. Следующие концепции дают понимание всех этих возможностей и помогают программисту определить, какую функциональность следует использовать в своих приложениях. Четыре из восьми базовых концепций MPI являются уникальными для MPI 2.
Коммуникатор
Объекты Communicator соединяют группы процессов в сеансе MPI. Каждый коммуникатор присваивает каждому входящему в него процессу независимый идентификатор и организует эти процессы в упорядоченную топологию. В MPI также существуют явные группы, но они в основном полезны для организации и реорганизации групп процессов перед созданием нового коммуникатора. MPI поддерживает операции внутри одной группы коммуникаторов и двустороннюю межкоммуникаторную связь. В MPI 1 наиболее распространены операции внутри одной группы. Двусторонние операции чаще встречаются в MPI 2, где они включают коллективные коммуникации и динамическое управление процессами. Коммуникаторы можно разделять с помощью нескольких команд MPI, включая MPI_COMM_SPLIT, в которой каждый процесс присоединяется к одному из нескольких подкоммуникаторов, определяемых цветом, указывая свой цвет.
Основы точечного общения
Ряд важных функций MPI включают в себя связь между двумя конкретными процессами. Популярным примером является MPI Send, который позволяет одному указанному процессу отправить сообщение другому указанному процессу. Операции "точка-точка", как они называются, особенно полезны при структурированном или нерегулярном обмене данными, например, в архитектуре с параллелизмом данных, где каждый процессор регулярно обменивается областями данных с определенными другими процессорами между шагами вычислений, или в архитектуре "главный-подчиненный", где главный процесс отправляет новые данные задачи подчиненному процессу, когда предыдущая задача завершена. MPI 1 определяет механизмы как для блокирующего, так и для неблокирующего обмена данными типа "точка-точка", а также так называемый механизм "подготовки к отправке", при котором запрос на отправку может быть выполнен только после того, как соответствующий запрос на прием уже был сделан.
Коллективные основы
Коллективные функции подразумевают обмен данными между всеми процессами в группе процессов (что может относиться ко всему пулу процессов или к подмножеству, определенному программой). Типичным примером является вызов MPI Bcast (сокращение от "broadcast"). Эта функция берет данные с одного узла и рассылает их всем процессам в группе процессов. Обратной операцией является вызов MPI Reduce, который собирает данные со всех процессов в группе, выполняет над ними операцию (например, суммирование) и сохраняет результат на одном узле. MPI Reduce часто бывает полезен в начале или в конце масштабных распределенных вычислений, когда каждый процессор обрабатывает часть данных, а затем результаты объединяются. Другие операции выполняют более сложные задачи, например, MPI Alltoall, который переупорядочивает n элементов данных таким образом, чтобы n-й узел получил n-й элемент данных от каждого процесса.
Одностороннее общение
MPI 2 определяет три односторонние операции связи: MPI Put, MPI Get и MPI Accumulate, представляющие собой запись в удаленную память, чтение из удаленной памяти и операцию сведения на одной и той же памяти между несколькими процессами соответственно. Также определены три различных метода синхронизации этих операций (глобальная, попарная и удаленные блокировки), поскольку спецификация не гарантирует завершения этих операций до достижения точки синхронизации. Такие вызовы часто полезны для алгоритмов, где синхронизация затруднительна (например, распределенное умножение матриц), или когда желательно, чтобы процессы могли балансировать нагрузку во время работы других процессоров над данными.
Динамическое управление процессом
Ключевым аспектом является "способность процесса MPI участвовать в создании новых процессов MPI или устанавливать связь с процессами MPI, запущенными отдельно". Спецификация MPI 2 описывает три основных интерфейса, посредством которых процессы MPI могут динамически устанавливать коммуникации: MPI Comm spawn, MPI Comm accept/MPI Comm connect и MPI Comm join. Интерфейс MPI Comm spawn позволяет процессу MPI порождать несколько экземпляров указанного процесса MPI. Новый набор MPI-процессов формирует новый интракоммуникатор MPI COMM WORLD, но может взаимодействовать как с родительским процессом, так и через интеркоммуникатор, возвращаемый функцией. MPI Comm spawn multiple – это альтернативный интерфейс, позволяющий порожденным экземплярам быть различными исполняемыми файлами с различными аргументами.
В/В
Параллельная функция ввода-вывода, иногда называемая MPI IO, представляет собой набор функций, предназначенных для абстрагирования управления вводом-выводом в распределенных системах посредством MPI и обеспечивающих легкий доступ к файлам в структурированном порядке с использованием существующей функциональности производных типов данных. Незначительное количество исследований, проведенных по этой функции, указывает на то, что получение существенного прироста производительности при использовании MPI IO может быть нетривиальной задачей. Например, реализация умножения разреженных матриц на вектор с использованием библиотеки MPI I/O демонстрирует в целом незначительное увеличение производительности, однако эти результаты не являются окончательными. Широкое распространение MPI IO началось лишь после реализации в ней коллективного ввода-вывода. Коллективный ввод-вывод значительно повышает пропускную способность приложений, поскольку процессы совместно преобразуют небольшие и несмежные операции ввода-вывода в большие и смежные, тем самым снижая накладные расходы на блокировку и перемещение головок диска. Благодаря значительным преимуществам в производительности, MPI IO также стал базовым уровнем ввода-вывода для многих современных библиотек ввода-вывода, таких как HDF5 и Parallel NetCDF. Его популярность также стимулировала исследования по оптимизации коллективного ввода-вывода, такие как ввод-вывод с учетом компоновки данных и агрегация между файлами.
until the idea of collective I/O implemented into MPI IO that MPI IO started to reach widespread adoption. Collective I/O substantially boosts applications' I/O bandwidth by having processes collectively transform the small and noncontiguous I/O operations into large and contiguous ones, thereby reducing the locking and disk seek overhead. Due to its vast performance benefits, MPI IO also became the underlying I/O layer for many state of the art I/O libraries, such as HDF5 and Parallel NetCDF. Its popularity also triggered research on collective I/O optimizations, such as layout aware I/O and cross file aggregation.
Официальные реализации
Первоначальной реализацией стандарта MPI 1.x был MPICH, разработанный в Национальной лаборатории Аргонн (ANL) и Университете штата Миссисипи. IBM также была одним из первых разработчиков, и большинство компаний, производивших суперкомпьютеры в начале 90-х годов, либо коммерциализировали MPICH, либо создали собственные реализации. LAM/MPI из Суперкомпьютерного центра Огайо была еще одной ранней реализацией с открытым исходным кодом. ANL продолжает разрабатывать MPICH уже более десяти лет и сейчас предлагает MPICH 3.2, реализующий стандарт MPI 3.1. Open MPI (не следует путать с OpenMP) был создан в результате объединения FT MPI, LA MPI, LAM/MPI и PACX MPI и используется во многих суперкомпьютерах, входящих в список TOP 500. Многие другие разработки являются производными от MPICH, LAM и других проектов, включая, помимо прочего, коммерческие реализации от HPE, Intel, Microsoft и NEC. Хотя спецификация требует наличия интерфейсов C и Fortran, язык, используемый для реализации MPI, не обязан соответствовать языку или языкам, которые он поддерживает во время выполнения. Большинство реализаций сочетают в себе C, C++ и ассемблер, и предназначены для программистов на C, C++ и Fortran. Существуют привязки для многих других языков, включая Perl, Python, R, Ruby, Java и CL (см. #Language bindings). ABI реализаций MPI в основном разделяются между производными от MPICH и Open MPI, поэтому библиотека из одной группы может быть использована как прямая замена другой из той же группы, но прямая замена между группами невозможна. Французское агентство CEA поддерживает оберточный интерфейс для упрощения таких переключений.
Оборудование
Исследования в области MPI-аппаратуры сосредоточены на непосредственной реализации MPI в аппаратном обеспечении, например, с помощью процессоров в памяти, или на встраивании операций MPI в микросхемы чипов оперативной памяти в каждом узле. Этот подход, по сути, не зависит от языка программирования, операционной системы и типа процессора, но его сложно обновлять или удалять. Другой подход заключается в добавлении аппаратного ускорения к одной или нескольким частям операций, включая аппаратную обработку очередей MPI и использование RDMA для прямой передачи данных между памятью и сетевым контроллером без участия центрального процессора или ядра операционной системы.
Оболочки для компиляторов
mpicc (а также mpic++, mpif90 и т.д.) — это программа, которая использует существующий компилятор, добавляя необходимые флаги командной строки при компиляции кода, использующего MPI. Обычно она добавляет несколько флагов, обеспечивающих компиляцию и связывание кода с библиотекой MPI.
Языковые связи
Привязки — это библиотеки, которые расширяют поддержку MPI для других языков, оборачивая существующую реализацию MPI, такую как MPICH или Open MPI.
Общая языковая инфраструктура
Две управляемые реализации Common Language Infrastructure .NET – Pure Mpi .NET и MPI .NET, являющиеся результатом исследовательской работы в Университете Индианы и распространяемые по лицензии в стиле BSD. Они совместимы с Mono и способны в полной мере использовать базовые сетевые среды MPI с минимальной задержкой.
Ява
Хотя в Java нет официальной привязки MPI, несколько групп пытаются объединить их, с разной степенью успеха и совместимости. Одной из первых попыток был mpiJava Брайана Карпентера – по сути, набор оберток Java Native Interface (JNI) для локальной C MPI библиотеки, что привело к созданию гибридной реализации с ограниченной переносимостью, которую также необходимо компилировать с использованием конкретной MPI библиотеки. Однако этот оригинальный проект также определил API mpiJava (фактически стандартный MPI API для Java, который тесно следовал эквивалентным привязкам C++), который был принят другими последующими Java MPI проектами. Менее распространенным API является MPJ API, который был разработан с учетом объектно-ориентированного подхода и соответствия стандартам кодирования Sun Microsystems. Помимо API, библиотеки Java MPI могут зависеть от локальной MPI библиотеки или реализовывать функции передачи сообщений непосредственно в Java, в то время как некоторые, такие как P2P MPI, также предоставляют функциональность типа "одноранговая сеть" и позволяют работать на смешанных платформах. Некоторые из наиболее сложных аспектов интеграции Java и MPI связаны с особенностями Java, такими как отсутствие явных указателей и линейного адресного пространства памяти для объектов, что делает передачу многомерных массивов и сложных объектов неэффективной. Обычно для решения этой проблемы используются методы передачи данных построчно и/или явная десериализация и приведение типов на отправляющей и принимающей сторонах, имитация массивов в стиле C или Fortran с помощью одномерных массивов и указателей на примитивные типы с помощью массивов из одного элемента, что приводит к стилям программирования, значительно отличающимся от общепринятых в Java. Другая система обмена сообщениями для Java – MPJ Express. Последние версии могут выполняться в кластерных и многоядерных конфигурациях. В кластерной конфигурации она может выполнять параллельные Java приложения на кластерах и в облаках. Здесь Java сокеты или специализированные межсоединения ввода-вывода, такие как Myrinet, могут поддерживать обмен сообщениями между процессами MPJ Express. Она также может использовать нативную C реализацию MPI с использованием ее собственного устройства. В многоядерной конфигурации параллельное Java приложение выполняется на многоядерных процессорах. В этом режиме процессы MPJ Express представлены потоками Java.
Джулия
Существует обертка для MPI на языке Julia.
MATLAB
Существует несколько академических реализаций MPI, использующих MATLAB. В MATLAB имеется собственная библиотека параллельных расширений, реализованная с помощью MPI и PVM.
ОКамм
Модуль OCamlMPI реализует значительную часть функций MPI и активно используется в научных вычислениях. Программа на OCaml, состоящая из 11 000 строк кода, была адаптирована для работы с MPI с использованием этого модуля, потребовав добавления 500 строк кода и незначительной реструктуризации, и продемонстрировала отличные результаты при запуске на суперкомпьютере, состоящем до 170 узлов.
ПАРИ/ГП
PARI/GP может быть скомпилирован с использованием MPI в качестве многопоточной подсистемы, что позволяет запускать параллельные программы PARI и GP на MPI-кластерах без изменений.
Python (англ.)
Реализации MPI в Python включают: pyMPI, mpi4py, pypar, MYMPI и подмодуль MPI в ScientificPython. pyMPI примечателен тем, что представляет собой вариант интерпретатора Python, а pypar, MYMPI и модуль ScientificPython – это модули для импорта. В этом случае программисту необходимо самостоятельно определить, где разместить вызов MPI Init. В 2006 году библиотеки Boost C++ приобрели Boost:MPI, который включал в себя привязки MPI для Python. Это особенно полезно при смешении C++ и Python. По состоянию на 2016 год привязки Python для Boost:MPI по-прежнему содержат неисправленные ошибки упаковки в CentOS.
R. В
R-связи MPI включают Rmpi и pbdMPI, при этом Rmpi ориентирован на параллелизм типа «менеджер-рабочие», а pbdMPI — на параллелизм SPMD. Обе реализации полностью поддерживают Open MPI или MPICH2.
Принятие MPI-2
Принятие MPI 1.2 было повсеместным, особенно в кластерных вычислениях, но внедрение MPI 2.1 оказалось более ограниченным. Проблемы включают следующее:
Реализации MPI 2 включают в себя операции ввода-вывода и динамическое управление процессами, что значительно увеличивает размер промежуточного программного обеспечения. Большинство установок, использующих системы пакетного планирования, не поддерживают динамическое управление процессами. Параллельный ввод-вывод MPI 2 получил широкое признание. Многие программы MPI 1.2 были разработаны до появления MPI 2. Изначально опасения по поводу переносимости замедляли внедрение, однако расширение поддержки ослабило их. Многие приложения MPI 1.2 используют лишь часть этого стандарта (16-25 функций) и не нуждаются в функциональности MPI 2.
Будущее
Некоторые аспекты будущего MPI представляются устойчивыми, другие – менее определенными. Форум MPI возобновил свою работу в 2007 году для уточнения некоторых вопросов MPI 2 и изучения возможностей для возможного MPI 3, что привело к созданию версий MPI 3.0 (сентябрь 2012 года) и MPI 3.1 (июнь 2015 года). Архитектуры меняются, характеризуясь большей внутренней параллельностью (многоядерность), улучшенным управлением мелкозернистой параллельностью (потоки, привязка) и большим количеством уровней иерархии памяти. Многопоточные программы могут легче использовать эти достижения, чем однопоточные приложения. Это уже привело к появлению отдельных, дополнительных стандартов для симметричной мультипроцессорности, в частности OpenMP. MPI 2 определяет, как стандартно-соответствующие реализации должны обрабатывать вопросы, связанные с многопоточностью, но не требует, чтобы реализации были многопоточными или даже потокобезопасными. MPI 3 добавляет возможность использования параллелизма общей памяти внутри узла. Реализации MPI, такие как Adaptive MPI, Hybrid MPI, Fine Grained MPI, MPC и другие, предлагают расширения стандарта MPI, направленные на решение различных задач в области MPI. Астрофизик Джонатан Дурси опубликовал статью, в которой назвал MPI устаревшим, ссылаясь на новые технологии, такие как язык Chapel, Unified Parallel C, Hadoop, Spark и Flink. В то же время, почти все проекты в рамках проекта Exascale Computing Project явно строятся на основе MPI; MPI продемонстрировал масштабируемость на крупнейших вычислительных системах на начало 2020-х годов и широко признается как остающийся актуальным на долгий срок.