Организация неблокирующих обменов данными между процессорами 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Организация неблокирующих обменов данными между процессорами



MPI обеспечивает возможность неблокированного выполнения операций передачи данных между двумя процессами. Наименование неблокирующих аналогов образуется из названий соответствующих функций путем добавления префикса I (Immediate). Список параметров неблокирующих функций содержит весь набор параметров исходных функций и один дополнительный параметр request с типом MPI_Request (в функции MPI_Irecv отсутствует также параметр status): возьмем как пример стандартный send - int MPI_Isend(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm, MPI_Request *request);

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

Проверка состояния выполняемой неблокирующей операции передачи данных выполняется при помощи функции: int MPI_Test(MPI_Request *request, int *flag, MPI_status *status),

где request - дескриптор операции, определенный при вызове неблокирующей функции, flag - результат проверки (=true, если операция завершена), status - результат выполнения операции обмена (только для завершенной операции). Операция проверки является неблокирующей, т.е. процесс может проверить состояние неблокирующей операции обмена и продолжить далее свои вычисления, если по результатам проверки окажется, что операция все еще не завершена. Возможная схема совмещения вычислений и выполнения неблокирующей операции обмена может состоять в следующем:

MPI_Isend(buf,count,type,dest,tag,comm,&request);

do {

MPI_Test(&request,&flag,&status)

} while (!flag);

Если при выполнении неблокирующей операции окажется, что продолжение вычислений невозможно без получения передаваемых данных, то может быть использована блокирующая операция ожидания завершения операции: int MPI_Wait(MPI_Request *request, MPI_status *status). Кроме рассмотренных, MPI содержит ряд дополнительных функций проверки и ожидания неблокирующих операций обмена: MPI_Testall - проверка завершения всех перечисленных операций обмена, MPI_Waitall – ожидание завершения всех операций обмена, MPI_Testany - проверка завершения хотя бы одной из перечисленных операций обмена, MPI_Waitany – ожидание завершения любой из перечисленных операций обмена,

MPI_Testsome - проверка завершения каждой из перечисленных операций обмена, MPI_Waitsome - ожидание завершения хотя бы одной из перечисленных операций обмена и оценка состояния по всем операци

17) Передача данных от одного процесса всем процессам программы (широковещательная рассылка данных)

Достижение эффективного выполнения операции передачи данных от одного процесса всем процессам программы (широковещательная рассылка данных) может быть обеспечено при помощи функции MPI:
int MPI_Bcast(void *buf, int count, MPI_Datatype type, int root,MPI_Comm comm), где
buf, count, type — буфер памяти с отправляемым сообщением (для процесса с рангом 0) и для приема сообщений (для всех остальных процессов);
root — ранг процесса, выполняющего рассылку данных;
comm — коммуникатор, в рамках которого выполняется передача данных.
Функция MPI_Bcast осуществляет рассылку данных из буфера buf, содержащего count элементов типа type, с процесса, имеющего номер root, всем процессам, входящим в коммуникатор comm.


Следует отметить:

1. функция MPI_Bcast определяет коллективную операцию, и, тем самым, при выполнении необходимых рассылок данных вызов функции MPI_Bcast должен быть осуществлен всеми процессами указываемого коммуникатора (см. далее пример программы);

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

3. все коллективные операции "несовместимы" с парными операциями — так, например, принять широковещательное сообщение, отосланное с помощью MPI_Bcast, функцией MPI_Recv нельзя, для этого можно задействовать только MPI_Bcast.

 

18) Параллельная программа суммирования числовых значений
#include
#include
#include
#include "mpi.h"
int main(int argc, char* argv[]){
double x[100], TotalSum, ProcSum = 0.0;
int ProcRank, ProcNum, N=100, k, i1, i2;
MPI_Status Status;
// Инициализация
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&ProcNum);
MPI_Comm_rank(MPI_COMM_WORLD,&ProcRank);
// Подготовка данных
if (ProcRank == 0) DataInitialization(x,N);
// Рассылка данных на все процессы
MPI_Bcast(x, N, MPI_DOUBLE, 0, MPI_COMM_WORLD);
// Вычисление частичной суммы на каждом из процессов
// на каждом процессе суммируются элементы вектора x от i1 до i2
k = N / ProcNum;
i1 = k * ProcRank;
i2 = k * (ProcRank + 1);
if (ProcRank == ProcNum-1) i2 = N;
for (int i = i1; i < i2; i++)
ProcSum = ProcSum + x[i];
// Сборка частичных сумм на процессе с рангом 0
if (ProcRank == 0) {
TotalSum = ProcSum;
for (int i=1; i < ProcNum; i++) {
MPI_Recv(&ProcSum,1,MPI_DOUBLE,MPI_ANY_SOURCE,0, MPI_COMM_WORLD, &Status);
TotalSum = TotalSum + ProcSum;
}
}
else // Все процессы отсылают свои частичные суммы
MPI_Send(&ProcSum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
// Вывод результата
if (ProcRank == 0)
printf("\nTotal Sum = %10.2f",TotalSum);
MPI_Finalize();
return 0;
}

 

 

19. Передача данных от всех процессов одному процессу. Операции редукции. Базовые (пpедопpеделенные) типы операций MPI для функций редукции данных

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

В качестве операций редукции данных могут быть использованы предопределенные в MPI операции. Помимо данного стандартного набора операций могут быть определены и новые дополнительные операции непосредственно самим пользователем библиотеки
Элементы получаемого сообщения на процессе root представляют собой результаты обработки соответствующих элементов передаваемых процессами сообщений, т.е.:

 

Yi = ¤ i=0n-1 xij, 0<=j<n
где ¤ есть операция, задаваемая при вызове функции MPI_Reduce

Следует отметить:

· функция MPI_Reduce определяет коллективную операцию, и, тем самым, вызов функции должен быть выполнен всеми процессами указываемого коммуникатора. При этом все вызовы функции должны содержать одинаковые значения параметров count, type, op, root, comm;

· передача сообщений должна быть выполнена всеми процессами, результат операции будет получен только процессом с рангом root;

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

Базовые (предопределенные) типы операций MPI для функций редукции данных
Операции
Описание
MPI_MAX - Определение максимального значения
MPI_MIN - Определение минимального значения
MPI_SUM - Определение суммы значений
MPI_PROD - Определение произведения значений
MPI_LAND - Выполнение логической операции "И" над значениями сообщений
MPI_BAND - Выполнение битовой операции "И" над значениями сообщений
MPI_LOR - Выполнение логической операции "ИЛИ" над значениями сообщений
MPI_BOR - Выполнение битовой операции "ИЛИ" над значениями сообщений
MPI_LXOR - Выполнение логической операции исключающего "ИЛИ" над значениями сообщений
MPI_BXOR - Выполнение битовой операции исключающего "ИЛИ" над значениями сообщений
MPI_MAXLOC - Определение максимальных значений и их индексов
MPI_MINLOC - Определение минимальных значений и их индексов
Не все сочетания типа данных type и операции op возможны, разрешенные сочетания перечисленые ниже:
Допустимый тип операндов для алгоритмического языка C
Целый, вещественный: MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD
Целый: MPI_LAND, MPI_LOR, MPI_LXOR
Целый, байтовый: MPI_BAND, MPI_BOR, MPI_BXOR
Целый, вещественный: MPI_MINLOC, MPI_MAXLOC

 

Синхронизация вычислений

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

 

int MPI_Barrier(MPI_Comm comm), где
comm — коммуникатор, в рамках которого выполняется операция.


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

 

27) Под производным типом данных в MPI можно понимать описание набора значений предусмотренного в MPI типа, причем в общем случае описываемые значения не обязательно непрерывно располагаются в памяти. Задание типа в MPI принято осуществлять при помощи карты типа (type map) в виде последовательности описаний входящих в тип значений, каждое отдельное значение описывается указанием типа и смещения адреса месторасположения от некоторого базового адреса, т.е. Часть карты типа с указанием только типов значений именуется в MPI сигнатурой типа. Сигнатура типа описывает, какие базовые типы данных образуют некоторый производный тип данных MPI и, тем самым, управляет интерпретацией элементов данных при передаче или получении сообщений. Смещения карты типа определяют, где находятся значения данных. Поясним рассмотренные понятия на следующем примере. Пусть в сообщение должны входить значения переменных: double a; /* адрес 24 */ double b; /* адрес 40 */ int n; /* адрес 48 */Тогда производный тип для описания таких данных должен иметь карту типа следующего вида:{(MPI_DOUBLE,0),(MPI_DOUBLE,16),(MPI_INT,24)}. Дополнительно для производных типов данных в MPI используется следующий ряд новых понятий: Нижняя граница типа, верхняя граница типа, протяженность типа. Согласно определению нижняя граница есть смещение для первого байта значений рассматриваемого типа данных. Соответственно верхняя граница представляет собой смещение для байта, располагающегося вслед за последним элементом рассматриваемого типа данных. При этом величина смещения для верхней границы может быть округлена вверх с учетом требований выравнивания адресов. Следует также указать на различие понятий протяженности и размера типа. Протяженность – это размер памяти в байтах, который нужно отводить для одного элемента производного типа. Размер типа данных - это число байтов, которые занимают данные (разность между адресами последнего и первого байтов данных).. Для получения значения протяженности и размера типа в MPI предусмотрены функции: int MPI_Type_extent(MPI_Datatype type, MPI_Aint *extent), int MPI_Type_size (MPI_Datatype type, MPI_Aint *size). Определение нижней и верхней границ типа может быть выполнено при помощи функций: int MPI_Type_lb (MPI_Datatype type, MPI_Aint *disp),int MPI_Type_ub(MPI_Datatype type, MPI_Aint *disp).Важной и необходимой при конструировании производных типов является функция получения адреса переменной: int MPI_Address (void *location, MPI_Aint *address) Для снижения сложности в MPI предусмотрено несколько различных способов конструирования роизводных типов: Непрерывный способ позволяет определить непрерывный набор элементов существующего типа как новый производный тип. Векторный способ обеспечивает создание нового производного типа как набора элементов существующего типа, между элементами которого существуют регулярные промежутки по памяти. При этом,размер промежутков задается в числе элементов исходного типа, в то время, как в варианте H-векторного способа этот размер указывается в байтах. Индексный способ отличается от векторного метода тем, что промежутки между элементами исходного типа могут иметь нерегулярный характер. Структурный способ обеспечивает самое общее описание производного типа через явное указание карты создаваемого типа данных. При непрерывном способе конструирования производного типа данных в MPI используется функция: int MPI_Type_contiguous(int count,MPI_Data_type oldtype,MPI_Datatype *newtype). При векторном способе конструирования производного типа данных в MPI используются функции: - int MPI_Type_vector (int count, int blocklen, int stride, MPI_Data_type oldtype, MPI_Datatype *newtype), где count – количество блоков, blocklen – размер каждого блока, stride – количество элементов, расположенных между двумя соседними блоками oldtype - исходный тип данных, newtype - новый определяемый тип данных.-int MPI_Type_hvector (int count, int blocklen, MPI_Aint stride,MPI_Data_type oldtype, MPI_Datatype *newtype)При индексном способе конструирования производного типа данных в MPI используются функции:MPI_Type_indexed, MPI_Type_hindexedКак отмечалось ранее, структурный способ является самым общим методом конструирования производного типа данных при явном задании соответствующей карты типа. Использование такого способа производится при помощи функции: MPI_Type_struct,

 

 

29) При индексном способе конструирования производного типа данных в MPI используются функции:

int MPI_Type_indexed (int count, int blocklens[], int indices[], MPI_Data_type oldtype, MPI_Datatype *newtype),

где

- count – количество блоков,

- blocklens – количество элементов в каждов блоке,

- indices – смещение каждого блока от начала типа (в количестве

элементов исходного типа),

- oldtype - исходный тип данных,

- newtype - новый определяемый тип данных.

int MPI_Type_hindexed (int count, int blocklens[], MPI_Aint indices[],

MPI_Data_type oldtype, MPI_Datatype *newtype). Как следует из описания, при индексном способе новый производный тип создается как набор блоков разного размера из элементов исходного типа, при этом между блоками могут иметься разные промежутки по памяти. Для пояснения данного способа можно привести пример конструирования типа для описания верхней треугольной матрицы размером nxn:

// конструирование типа для описания верхней треугольной матрицы for (i=0, i<n; i++) {

blocklens[i] = n - i;

indices[i] = i * n + i;

}

MPI_Type_indexed (n, blocklens, indices, &UTMatrixType, &ElemType).Как и ранее, способ конструирования, определяемый функцией MPI_Type_hindexed, отличается тем, что элементы indices для определения интервалов между блоками задаются в байтах, а не в элементах исходного типа данных. Следует отметить, что существует еще одна дополнительная функция MPI_Type_create_indexed_block индексного способа конструирования для определения типов с блоками одинакового размера (данная функция предусматривается стандартом MPI-2).



Поделиться:


Последнее изменение этой страницы: 2016-04-08; просмотров: 521; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.138.174.95 (0.03 с.)