Понятие 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:
-
Инициализация операции — функции с префиксом
MPI_*_init(…, comm, req) -
Запуск операции — функции
MPI_Start(req) -
Завершение операции — блокирующий вызов
MPI_Wait(req)или неблокирующая функцияMPI_Test(req) -
Освобождение ресурсов —
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".