Microsoft mpi что это за программа и нужна ли она

Обновлено: 04.07.2024

В этом цикле статей речь идет о параллельном программировании с использованием MPI.

Часть 1. MPI - Введение и первая программа.

Часть 2. MPI - Учимся следить за процессами.

В предыдущей статье мы обсудили как запускать программу, что такое MPI и зачем нужно это параллельное программирование, если можно писать и без него. В этой статье, предпологаем, что читатель ознакомился с материалом, изложенным в предыдущей и приступаем к следующему шагу изучения технологии MPI, а именно управлению процессами. Дабы избежать негодования опытных программистов далее я буду иметь ввиду под "потоками", "процессами" и т.п. часть вычислительной системы на которой запущен конкретный экземпляр программы (Этой частью может быть как конкретный поток, так и любой вычислительный узел системы).

Номера процессов и общее число процессов

Чтобы выполнять полезные действия при построении параллельной программы необходимо распределять роли между вычислительными узлами, потоками. Для этого нам просто жизненно необходимо знать какой поток обрабатывает конкретный экземпляр запущенной на нем программы, но для начала неплохо было бы узнать сколько их запущено вообще.

Для того чтобы узнать на каком потоке запущена программа существует процедур MPI_Comm_size. Она принимает на вход коммуникатор(о нем пойдет речь далее), и адрес памяти куда будет записано целое число, то есть количество потоков обрабатывающих программу.

Так что такое коммуникатор и зачем он собственно нужен? Коммуникатор это такой объект, который хранит в себе информацию о запущенных потоках, доступ к которым ему предоставлен. Роль коммуникатора в программе очень важна, так как большая часть работы с процессами связана именно через него, на то он и называется коммуникатором. В MPI существует глобальный коммуникатор который имеет доступ ко всем запущенным потокам, его название MPI_COMM_WORLD. Также мы можем создавать свои, локальные коммуникаторы для выполнения определенных задач на конкретных потоках, и это довольно мило.

Что такое коммуникатор разобрались, теперь было бы неплохо узнать на каком из процессов работает конкретный экземпляр программы. Для этого существует процедура MPI_Comm_size. Она принимает на вход аналогичные параметры, только вместо сохранения количества процессов она сохраняет по адресу номер конкретного процесса. Определена она вот так:

То есть мы передаем ей коммуникатор в котором надо узнать номер процесса и собственно адрес куда его нужно записать.

Теперь предлагаю соединить эти 2 важные процедуры и посмотреть на их работу на практике, пока еще конечно на бесполезной программе, но понять как работают эти процедуры она позволит вполне.

Выход для 5 потоков будет следующим:

Как видим каждый поток напечатал свой номер процесса и общее число запущенных процессов.

Как это можно использовать? Я думаю вы уже догадались, что уже имея только эту информацию можно использовать возможности параллельного выполнения программы. Допустим у нас есть задача где довольно много однотипных независимых вычислений, будь то сложение, вычитание матриц, векторов, возведение в степень большого числа чисел и т.п. То есть те задачи, где вычисления никак не зависят друг от друга.

Работа Comm_size, Comm_rank на примере

Более полезным примером такого распараллеливания будет поиск квадратов чисел в заданном диапазоне. Далее идет небольшой пример программы которая это и делает.

Выход для 5 потоков:

Дабы не вставлять огромные участки с кодом я взял число MAX=20. Как видим зная номер процесса на котором исполняется конкретный экземпляр программы уже дает ощутимые возможности, но это далеко не все, ведь сложные задачи далеко не всегда так легко делятся на независимые участки с вычислениями.

Важное замечание, которое вы, возможно, заметите: процессы совсем не обязательно будут выводить в консоль в правильном порядке свои результаты. MPI гарантирует лишь корректность вывода в плане целостности данных, то есть дается гарантия что строка будет выведена целиком, но порядок не гарантируется.

Пологаю, что все кто попробуют запустить такую программу заметят, что выполнение начинается не сразу, а иногда даже из-за этого программа на одном потоке работает горазда быстрее чем параллельная. С виду кажется не совсем логичным, если не знать о том как работает параллельное выполнение, однако на все есть свои веские причины. Когда срабатывает процедура MPI_Init, довольно большое количество ресурсов тратится на то чтобы собственно породить эти параллельные потоки и создать среду для их корректного выполнения и именно поэтому маленькие программы и мелкие, краткосрочные вычисления, мало того что не требуют подобной оптимизации, так еще и выполняются медленнее из-за того что время порождения потоков занимает существенную часть из времени всех вычислений, а иногда даже и больше чем сами вычисления. Именно поэтому стоит несколько раз подумать нужно ли распараллеливать данный участок вообще.

Работа со временем

Кстати касательно времени. Чтобы работать со временем в MPI можно использовать как стандартные функции из библиотеки <time>, так и процедуры параллельной библиотеки.

Первая процедура возвращает на вызвавшем ее процессе астрономическое время в секундах, прошедшее с некоторого момента в прошлом. Какой это конкретно момент не совсем имеет значения, но гарантируется, эта точка отсчета не изменится в течение всего времени выполнения программы. Зная эту процедуру можно довольно легко определить время выполнения конкретного участка кода, ведь разность между Wtime в конце и начале программы как раз его и определяет, все стандартно и вполне знакомо.

Таймеры разных процессов не всегда могут быть синхронизированы, для того чтобы узнать так это или нет можно вызвать глобальную переменную MPI_WTIME_IS_GLOBAL, она вернет 0 или 1, то есть не синхронизированы или синхронизированы.

Вторая процедура как раз возвращает разрешение таймера конкретного процесса в секундах.

И на последок покажу как узнать имя физического процессора на котором выполняется программа. Для этого есть процедура MPI_Get_processor_name. Синтаксис и параметры вот такие:

На вход она принимает соответственно ссылку на область памяти куда нужно записать имя физического процессора и ссылку куда будет записана длина этой строки.

Резюмируем

В данной статье я наглядно показал как можно работать с процессами в MPI, зачем нам узнавать на каком процессе запущена программа и как работать со временем. Имея в багаже эти знания уже можно вполне успешно создавать простые параллельные программы и управлять ими.

Для закрепления полученных знаний предлагаю написать вам простую программу, которая узнает является ли число простым для чисел в заданном диапазоне от 1 до N. Это наглядно покажет вам как можно легко и просто распараллелить вычисления с помощью данной технологии и позволит отложить все полученные навыки в голове.

Всем приятного времени суток, хабравчане и те кто набрел на эту статью извне.

MPI. Пример

Реализации MPI

MPI CHameleon (MPICH)

Свободно распространяемая реализация MPI. Пакет доступен в исходных кодах, поэтому допускает гибкую настройку. Поддерживается работа в различных версиях ОС UNIX, Mac OS и в последних версиях Microsoft Windows.

MPICH соответствует спецификации MPI-2. Поддерживаются различные коммуникационные среды (в т.ч. 10 Gigabit Ethernet, InfiniBand, Myrinet, Quadrics). Пока не поддерживаются системы, гетерогенные по форматам хранения данных. Имеется версия с поддержкой пакета Globus.

LAM (Local Area Multicomputer) MPI

"Opensource" реализация MPI, соответствующая спецификации MPI-1 и, в значительной мере, спецификации MPI-2. LAM поддерживает гетерогенные конфигурации, поддерживает пакет Globus и удовлетворяет IMPI (Interoperable MPI). Поддерживаются различные коммуникационные системы (в т.ч. Myrinet).

  • LAM/MPI
  • MPI/Pro
  • Hewlett-Packard MPI (от версии 1.7)
  • GridMPI

LAM может работать на метакластерных системах.

Сайт в Интернете:

Microsoft MPI

Поддерживается спецификация MPI-2.

OpenMPI

"Opensource" реализация MPI-2, разрабатываемая консорциумом представителей академических, научных и индустриальных кругов.

  • Полное соответствие спецификации MPI-2.
  • Поддержка различных ОС.
  • Поддержка различных коммуникационных сред.

Инструменты Intel

Библиотека Intel ® Math Kernel Library (MKL)

  • BLAS (3 уровня + расширение – уровень 1 для разреженных векторов)
  • LAPACK – вычислительная алгебра, в том числе решение спектральных задач
  • DFT (дискретное преобразование Фурье) – в том числе многомерное. Многопоточная реализация
  • Vector Mathematical Library – математические функции
  • Vector Statistical Library – набор векторизованных генераторов случайных чисел
  • Солверы, предобуславливатели, средства поддержки численного решения дифференциальных уравнений и др.

Оптимизирована для архитектуры Intel ®

Intel ® Integrated Performance Primitives (IPP)

Библиотека готовых компонентов для разработки мультимедийных приложений для вычислительных платформ Intel.

Включает модули для обработки сигналов и выполнения векторных и матричных операций, функции сжатия и распаковки речи и статических/динамических изображений, средства шифрования и обработки аудиоданных и текстовых строк и другое.

Intel IPP обеспечивает прозрачное использование расширенных возможностей процессоров Intel, таких, как технология MMX, наборы команд Streaming SIMD Extensions. Библиотека Intel IPP оптимизирована для работы с процессорами компании Intel.

Библиотека Intel IPP поддерживает 32- и 64-битные операционные системы Windows и Linux, включая встраиваемые версии, такие как Windows Mobile.

Intel ® Threading Building Blocks (TBB)

Библиотека готовых шаблонов C++, упрощающая разработку многопоточных приложений, обеспечивая более высокий уровень абстракции при распараллеливании.

Многоплатформенность: Linux, Microsoft Windows, Mac OS.

Если код написан на языке C++, лучше Intel® TBB. Intel® TBB хорошо подходит, если код в значительной степени объектно-ориентирован и в нем широко используются шаблоны C++ и определяемые пользователем типы.

Если код написан на C или Fortran, лучше выбрать OpenMP, поскольку этот API лучше соответствует стилю структурного программирования.

При использовании C++, если в программе преобладают операции обработки массивов, OpenMP может оказаться удобнее с точки зрения сложности программирования.

Компиляторы

Использование возможностей автоматической оптимизации компилятора может дать значительный выигрыш в производительности. Компиляторы Intel предоставляют большие возможности автоматической оптимизации приложений.

Intel® VtuneTM Amplifier XE

Intel ® VtuneTM Amplifier XE – программный инструмент, позволяющий выявить и локализовать проблемы производительности ПО.

  • сбор различных показателей производительности;
  • отображение данных в различных режимах (system-wide, исходный код и процессорные инструкции);
  • выявление потенциальных проблем производительности и создание рекомендаций по их разрешению.

Intel® Cluster Studio XE

Intel® Parallel Studio

Выявление "кандидатов" на распараллеливание

  • Intel® C++ Compiler, Intel® Threading Building
  • Blocks, Intel® Integrated Performance Primitives,
  • and Intel® Parallel Debugger Extension.

Выявление ошибок использования памяти (утечки памяти, переполнение буфера, указатели) и многопоточности (блокировки, гонки за данными и т.д.) на основе анализа выполнения программы.

Анализ производительности, профилирование и оптимизация параллельных приложений

Другие инструменты

Intel® CilkTM Plus

Средство разработки приложений для вычислительных систем с общей памятью.

Средство разработки приложений, использующих в качестве ускорителей вычислений графические процессоры общего назначения.

Конфигурация параллельной среды MPI (Windows 10 + VSIDE Community 2019)

оглавление

  1. Перейти кMS-MPI Downloads , На официальной домашней странице нет упрощенной китайской версии этой страницы.
  2. Щелкните зеленую ссылку на картинке выше (новые версии могут быть выпущены в будущем), чтобы перейти на страницу загрузки.
  3. Выберите оранжевый вариант на рисунке выше, чтобы войти в интерфейс предварительного просмотра задачи.
  4. На картинке выше выберите все пакеты и загрузите их.
  5. После загрузки файла должны существовать следующие 2 файла (из Git Bash):
  6. Установить 2 инсталляционных пакета отдельно,Прежде чем выполнять все инструкции в этом блоге, запомните путь установки.!

  1. Запустите основную программу IDE Visual Studio.
  2. выбирать Создать новый проект Войти в каталог шаблонов проекта.
  3. выбирать Мастер рабочего стола Windows , Выбрать Следующий шаг И войдите в конфигурацию создания проекта.
  4. После заполнения всех конфигураций завершите процесс создания проекта и войдите в среду разработки проекта.
  1. Щелкните правой кнопкой мыши имя проекта и выберите Атрибуты Войдите на страницу конфигурации проекта. Использовать Alt + Enter Вы также можете быстро получить доступ к конфигурации проекта.
  2. Убедитесь, что платформа, на которую ориентирован проект, x64 。
  3. Разверните панель категорий конфигурации слева и найдите категорию конфигурации. Свойства конфигурации => C / C ++ => Общие ,неисправность SDL проверка 。
  4. Перейти к Свойства конфигурации => C / C ++ => определение препроцессора , Отредактируйте конфигурацию и добавьте MPICH_SKIP_MPICXX Параметры.

  5. Перейти к Свойства конфигурации => C / C ++ => генерация кода , Переключатель Библиотека времени исполнения За Многопоточная отладка (/ MTd) 。
  6. Перейти к Свойства конфигурации => компоновщик => ввод , Редактировать Дополнительные зависимости И добавить msmpi.lib 。

  7. Перейти к Атрибут конфигурации => Каталог VC ++ , Редактировать Включить каталог И добавить $MPI_SDK_HOME\Include ,среди них $MPI_SDK_HOME Это каталог установки Microsoft MPI SDK.

  8. Перейти к Атрибут конфигурации => Каталог VC ++ , Редактировать Каталог библиотеки И добавить $MPI_SDK_HOME\Lib\x64 ,среди них $MPI_SDK_HOME Это каталог установки Microsoft MPI SDK.

  9. применение а также определить Сохраните конфигурацию.
  1. Щелкните правой кнопкой мыши Исходный файл , Добавить => новый элемент , Выбрать Заголовочный файл C ++ 。
  2. Напишите следующий исходный код
  1. выбирать Build => построить проект , Visual Studio IDE помогает завершить компиляцию и генерацию проекта.

  2. Поскольку Visual Studio IDE поддерживает только последовательные программы, вы не можете запускать программы напрямую через VSIDE. выбирать Инструменты => Командная строка => Командная строка разработчика Чтобы войти в интерфейс командной строки.

  3. Использовать mpiexec -n $PROCESS_COUNT x64\Debug\$TARGET.exe Запустить программу, где $PROCESS_COUNT Для любого указанного количества процессов $TARGET Имя исполняемого файла, созданного для компиляции.

claster_mpi

Кластерная архитектура

Содержание:

Ключевые особенности MPI

Допустим, есть у нас кластер. Чтобы программа начала на нем выполняться, ее необходимо скопировать на каждый узел, запустить и установить связь между процессами. Эту работу берет на себя утилита mpirun (под Linux) или mpiexec (под Windows), так например, чтобы запустить 5 процессов достаточно написать:

mpirun -np 5 path/your_mpi_program

Однако программа должна быть написана определенным образом. Вообще, технология MPI позволяет как использовать модель SPMD (Single Process, Multiple Data), так и MPMD [1], в этой статье я рассматривают только первый вариант. Далее по тексту узел и процесс будут означать одно и тоже, хотя на одном узле может быть создано несколько процессов (именно так я делаю при запуске примеров статьи, т.к. отлаживаю их на персональном компьютере). Это вводная статья, поэтому тут не пойдет речь о коммуникаторах, в которые могут группироваться процессы.

В связи с тем, что MPI-программа обладает множеством особенностей, компилироваться она должна специальным компилятором. Под Linux для этого используется mpic++, а под Windows можно применять расширение для Microsoft Visual Studio. Для сборки примеров статьи под Linux я использовал примерно следующую команду:

mpic++ main.cpp -o main

Операции точка-точка MPI

Рассмотрим первый пример:

Все обращения к функциям MPI должны размещаться между MPI_Init и MPI_Finalize. В качестве коммуникатора в этом примере используется MPI_COMM_WORLD, который включает в себя все процессы, запущенные для текущей задачи с помощью mpirun. Каждый процесс получает свой ранг с помощью вызова MPI_Comm_rank, количество процессов в коммуникаторе возвращает функция MPI_Comm_size.

Функции неблокирующего обмена имеют такие же аргументы как и их блокирующие аналоги, но в качестве дополнительного параметра принимают параметр типа MPI_Request. Чтобы нулевой процесс в приведенном выше примере работал асинхронно достаточно изменить лишь этот фрагмент кода:

Теперь функция передачи возвращает управление немедленно, сама же передача происходит параллельно с выполнением других команд процесса. Узнать о ходе выполнения асинхронной операции мы сможем с помощью специальных функций:

Чтобы избежать ошибок, необходимо представлять как именно может быть реализована работа неблокирующих функций. Например, MPI_Isend инициирует передачу данных, которая выполняется в отдельном потоке параллельно. По окончанию передачи этот поток должен изменить переменную request. Это значит, что такой код вполне может привести к ошибкам:

При использовании буферизованного режима исходный код будет не сильно отличаться от приведенного выше, ниже приведен только фрагмент, которого коснулись изменения:

Коллективные операции. Пример использования MPI_Reduce

Коллективные операции выполняются всеми процессами указанного коммуникатора. Ниже приведена картинка из стандарта [3], на которой показана суть некоторых операций:

MPI_collective_communication

Коллективные операции MPI

Операция MPI_Bcast теоретически (зависит от реализации библиотеки) может работать более эффективно и выполняться за \(O(log(n))\) операций вместо \(O(n)\).

MPI_Reduce_and_Bcast_efficiency

Эффективная реализация MPI_Reduce и MPI_Bcast

На приведенной схеме цветом выделен узел, на котором находятся передаваемые данные. В начале работы такой узел один. После первой передачи данные есть уже на двух узлах, оба они могут участвовать в передачи. При реализации такой схемы для передачи данных на 1000 узлов будет достаточно 10 операций. Таким же образом может работать операция MPI_Reduce:

A more efficient implementation is achieved by taking advantage of associativity and using a logarithmic tree reduction. [3]

Операция MPI_Reduce не просто передает данные, но и выполняет над ними заданную операцию. В нашем примере применить ее можно вместо сбора результатов вычисления сумм:

Операция MPI_Reduce может выполняться не только над числами, но и над массивами (при этом будет применена к каждому его элементу отдельно).

Заключение

Пример с функцией MPI_Isend наглядно демонстрирует насколько сложно реализовать аналогичное поведение вручную. Дело не только в том, что передача выполняется в отдельном потоке. Сложно придумать механизм лучший, чем работа с MPI_Request, однако к объекту request может параллельно обращаться несколько потоков, поэтому все эти операции защищаются семафорами.

Читайте также: