Понятие MPI операции

В разделе "2.4.1 MPI Operations" стандарта вводится понятие MPI операции и выделяются этапы её выполнения (stages).

MPI операция (MPI Operation) — последовательность шагов, выполняемых библиотекой MPI для подготовки и запуска передачи данных и/или синхронизации процессов. Каждая операция включает четыре этапа (stages), которые реализованы в виде одной или нескольких функций MPI: инициализация (initialization), запуск (starting), завершение (completion) и освобождение ресурсов (freeing):

  • Initialization — код на стороне пользователя передаёт операции аргументы, но не содержимое буферов с данными, если таковые имеются. В спецификации может быть дополнительно указано, что аргументы не должны изменяться пока операция не будет завершена, а ресурсы освобождены.

  • Starting — операции передается контроль над содержимым буферов с данными; комбинация этапов инициализации и запуска называется инициацией (initiation); запущенная коммуникационная операция называется активной (active, pending);

  • Completion — операция возвращает управление пользовательскому коду содержимым буферов данных и указывает, что выходные аргументы, если имеются, обновлены; MPI операция считается завершенной (complete), когда соответствующая функция MPI, реализующая этап завершения, вернула управление пользовательскому коду.

  • Freeing — операция выполняет внутренние процедуры освобождения ресурсов и возвращает управление пользовательскому коду значениями остальных аргументов (адреса буферов, вспомогательные массивы).

По способу реализации отдельных этапов стандарт определяет три типа операций: блокирующие (blocking), неблокирующие (nonblocking), постоянные (persistent).

Реализация этапов в виде отдельных функций позволяет реализовывать пользователю совмещение передачи сообщений и вычислений (computation/communication overlap), а также сократить накладные расходы на инициализацию операции, если она многократно повторяется без изменения её аргументов.

Блокирующие операции

Блокирующая операция (blocking) — все четыре этапа Create Start Complete Free реализованы в одной функции; управление пользовательскому коду не возвращается пока операция не будет завершена и не освободит ресурсы. Примеры:

  MPI_Send(buf, count, datatype, dest, tag, comm)
  MPI_Bcast(buf, count, datatype, root, comm)

Неблокирующие операции

Неблокирующая операция (nonblocking) — инициализация и запуск операции объединены в одной функции с префиксом MPI_I, завершение операции и освобождение ресурсов аналогично объединены в одной блокирующей функции MPI_Wait или неблокирующей функции MPI_Test.

Пример неблокирующей инициации операции и завершения блокирующим вызовом MPI_Wait:

  MPI_Isend(..., req)
  MPI_Wait(req)

Пример неблокирующей инициации операции и завершения циклическим обращением к неблокирующей функции MPI_Test:

  MPI_Isend(..., req)
  do {
    ...
    MPI_Test(req, flag, status);
  } while(flag != true)

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

Постоянные операции

Постоянная операция (устойчивая, неизменяемая, долгосрочная, persistent) — каждый этап реализован в виде отдельной функции, блокирующей или неблокирующей; в такой операции значения аргументов не могут быть изменены после инициализации (за исключением содержимого буферов).

Последовательность вызовов Create (Start Complete)∗ Free:

  1. Инициализация операции — функции с префиксом MPI_*_init(…​, comm, req)

  2. Запуск операции — функции MPI_Start(req)

  3. Завершение операции — блокирующий вызов MPI_Wait(req) или неблокирующая функция MPI_Test(req)

  4. Освобождение ресурсов — MPI_Request_free(req)

Пример многократного запуска операции MPI_Send в цикле:

  MPI_Send_init(buf, count, datatype, dest, tag, сomm, req)
  for (;;) {
    // Вычисление buf
    MPI_Start(req)
    MPI_Wait(req)
  }
  MPI_Request_free(req)

Запуска операции MPI_Allreduce:

  MPI_Info info;
  MPI_Info_create(info);
  MPI_Info_set(info, "mpi_assert_strict_persistent_collective_ordering", "true");

  MPI_Allreduce_init(sendbuf, recvbuf, count, datatype, MPI_MAX, comm, info, req)
  for (;;) {
    ...
    MPI_Start(req)
    MPI_Wait(req)
  }
  MPI_Request_free(req)

  MPI_Info_free(info);

Разбор аргументов операции, создание дескриптора запроса выполняется один раз на этапе инициализации. При многократных вызовах операции в цикле это позволяет сократить накладные расходы.

Постоянные коллективные операции имеют дополнительный аргумент MPI_Info info, в котором пользователь может передать подсказку библиотеке (optimization hint) по оптимизации выполнения. Дополнительную информацию можно найти в разделе "7.4.4 Communicator Info".