Интерфейс для коллективных межпроцессорных обменов

Инициализация и завершение

Функции инициализации и завершения CENTAUR_Init и CENTAUR_Finalize аналогичны соответствующим функциям из библиотеки MPI.

// Инициализация MPI
void CENTAUR_Init (int argv, char ** argc);

// Завершение работы MPI
void CENTAUR_Finalize ();


Функция CENTAUR_Init инициализирует MPI, запоминает во внутренних переменных ранг текущего процесса и общее количество процессов.

Переключение режима

В процессе работы на основном (главном) процессе хранятся две копии массивов, описывающих сетку и данные. Одна копия — глобальная — относится ко всей сетке и используется для чтения и записи данный в файл. Вторая копия — локальная — относится только к той части сетки, которая распределена на данный процесс, и используется для вычислений. Существует также два режима выполнения процесса: локальный режим, в котором доступна локальная копия массивов и глобальный режим, в котором доступна, соответственно, глобальная копия. Реализация этого механизма такова, что обращаясь одной и той же переменной массива, получаем ссылку либо на глобальный, либо на локальный массив в зависимости от того, в каком режиме находится процесс.
Для переключения между режимами используются функции CENTAUR_Switch_Global и CENTAUR_Switch_Local. Эти функции являются коллективными (в терминах MPI) и должны вызываться одновременно всеми процессами.

// Переключение в глобальный режим
int CENTAUR_Switch_Global ();

// Переключение в локальный режим
int CENTAUR_Switch_Local ();


В глобальном режиме данные доступны только на основном процессе. Для проверки, что процесс основной используется функция CENTAUR_IsMain (имеет ранг 0 в терминах библиотеки MPI).

// Проверка того, что текущий процесс — главный.
// Только из главного можно работать с файлами.
int CENTAUR_IsMain () {

 return 0 == CENTAUR_comm_rank;

}


В глобальном режиме основной процесс может работать с файлами так, как это происходило в последовательной версии программы.

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

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

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

Распределение данных

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

// Распределение данных
void CENTAUR_Scatter ();


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

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

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

Сбор данных

Для сбора всех данных на одном основном процессе, например, для записи их в файл, используется функция CENTAUR_Gather.

// Собирает все данные на основном процессе
void CENTAUR_Gather ();


Эта функция собирает все данные на главном процессе. Вызов CENTAUR_Gather можно сделать, 
находясь в любом режиме, и режим работы при этом не изменится.

Сеточные операции

В программах часто бывает необходимо пересчитывать координаты ячеек или ребер в номер элемента в массиве. Для организации такого пересчета используются функции CENTAUR_GNode, CENTAUR_GxRidge и CENTAUR_GyRidge. В зависимости от режима эти функции выдают номер элемента либо в глобальном массиве всей сетки (в глобальном режиме), либо в локальном массиве сетки, распределенной на данный процесс (в локальном режиме).

// Переводит координаты ячейки в индекс
int CENTAUR_GNode (int, int);

// Переводит координаты X-ребра в индекс
int CENTAUR_GxRidge (int, int);

// Переводит координаты Y-ребра в индекс
int CENTAUR_GyRidge (int, int);

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

Коллективные функции CENTAUR_Reduce* используются для вычисления глобальных функций (максимум, минимум, сумма, произведение...).

Коллективная функции CENTAUR_Update используется для обмена теневыми гранями.

// Редукция - вычисление максимума
void CENTAUR_ReduceMax (double *);

// Редукция - вычисление минимума
void CENTAUR_ReduceMin (double *);

// Редукция - вычисление суммы
void CENTAUR_ReduceSum (double *);

// Редукция - вычисление произведения
void CENTAUR_ReduceMul (double *);


// Обмен теневыми гранями
void CENTAUR_Update ();


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

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