Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лекции / Лекция 10

.doc
Скачиваний:
32
Добавлен:
06.07.2016
Размер:
21.5 Кб
Скачать

Технологии и методы программирования. Лекция 10.

Использование модулей.

Специфика MPI – все параллельные вычисления в одном модуле. Но это приводит к увеличению объёма кода на процессорах. Для борьбы с этим явлением применяются модули.

Пример:

MPI_Comm_rank(MPI_COMM_WORLD, *Proc_Rank);

if (Proc_Rank==0) DoMod0();

else if (Proc_Rank==1) DoMod1();

Такие стандартные структуры делают код более понятным.

Определение времени выполнения программы.

В стандарте MPI определены специальные функции определения времени выполнения. Их использование позволяет измерять время независимо от среды выполнения.

Прототип основной функции: double MPI_Wtime( void ). Измерение производится следующим образом: вычисляется время в начале выполнения операций и в конце, а потом одно вычитаем из другого. Другая функция - double MPI_Wtiсk( void ) - возвращает время между двумя отсчётами таймера.

Контроль выполнения программ.

Почти все функции MPI возвращают код завершения. По нему можно контролировать выполнение. MPI_SUCCESS – удачное выполнение, MPI_ERR – ошибка. Возможны варианты:

MPI_ERR_BUFFER – неверное использование буфера.

MPI_ERR_TRUNCATE – несоответствие размеров буферов отправителя и получателя

MPI_ERR_COMM – неверное использование коммуникатора

MPI_ERR_RANK – неверный ранг...

Все возможные ошибки описаны в спецификации и в mpi.h. При возникновении ошибок программа снимается с исполнения. Рекомендуется обрабатывать такие ситуации средствами MPI. Специально для этого существует MPI_About(MPI_COMM comm, int errcode). Можно указать код ошибки MPI_ERR_OTHER, тогда при любой ошибке программа завершится.

Простейшие коллективные операции передачи данных.

Один из ключевых моментов в MPI – синхронизация почти полностью за счёт сообщений. При этом передача данных может оказаться узким местом программы при распределённых вычислениях на кластере. Пример — вычисление суммы последовательности:

Схема: главный процесс инициирует последовательность в виде вектора и рассылает их по исполнителям. Процесс-исполнитель определяет интервал, для которого он вычисляет частичную сумму, а затем, вычислив её, отсылает результат главному процессу, на котором уже считается результат. Ключевой момент коммуникации — рассылка вектора. Это можно сделать и с помощью MPI_Send:

for (int i=0; i<Proc_Num-1; i++){

MPI_Send(&X, n, MPI_DOWBLE, I, 0, MPI_COMM_WORLD);

}

При этом присутствуют коммуникационные затраты: t(M)=tн+m*tк, где tк — передача 1 элемента, tн - время подготовки к передаче. Это повторится Proc_Num-1 раз, и столько же раз посчитается время подготовки. Гораздо лучше бы один раз подготовить сообщение. Это можно сделать с помощью коллективной рассылки: MPI_Bcast(void* buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm), где root — ранг отправителя. Функция должна быть вызвана у всех процессоров одновременно, при этом процесс ранга root будет отправителем, а остальные — получателями.

Пример:

int main(int argc, char* argv[]){

double X[100];

int Proc_Num, Proc_Rank, n=100, k, I, i1, i2.

Double TotalSum=0, ProcSum=0;

MPI_Status status;

MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORD, *Proc_Num);

MPI_Comm_rank(MPI_COMM_WORLD, *Proc_Rank);

if (Proc_Rank==0) Init(X, n);

MPI_Bcast(&x, n, MPI_DOUBLE, 0, MPI_COMM_WORLD);

k=n/Proc_Num;

i1=k*Proc_Rank;

i2=k*(Proc_Rank+1)

if (Proc_Rank == Proc_Num-1) i2=n;

Это не лучший вариант — но самый простой. Лучше — распределить остаток равномерно, или, если если порядок получения детерминирован, передать остаток в первый процесс.

for(i=i1; i<i2; i++){

ProcSum+=X[i];

}

if (Proc_Rank==0){

TotalSum=ProcSum;

for (i=1; i<Proc_Num-1; i++){

MPI_Recv(&ProcSum, 1, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status);

TotalSum+=ProcSum;

}

} else{

MPI_Send(&ProcSum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);

}

if (Proc_Rank==0){

printf(TotalSum);

}

MPI_Finalize();

return 0;

Можно также использовать MPI_Reduce для сбора данных на одном процессоре: MPI_Reduce(void *sendbuf, void *recbuf, int count, MPI_Datatype type, MPI_Op op, int root, MPI_Comm comm), где root — получатель, а op определяет оператор, который будет выполняться каждый раз при сборе данных. Для определения оператора существуют предопределённые константы. К примеру:

MPI_SUMM - суммирование

MPI_PROP - умножение

MPI_MAX – выбор максимального

MPI_MIN - выбор минимального

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

Соседние файлы в папке Лекции