- •Федеральное агентство по атомной энергии
- •Национальный исследовательский ядерный университет «мифи»
- •Средства разработки параллельных приложений на общей и распределенной памяти в стандарте интерфейса передачи данных mpi и openmp c реализации курс лекций
- •Void main(int argc, char *argv[] ){
- •Int rank, size;
- •Int mpi_Send(void* buf, int count, mpi_Datatype datatype, int dest, int tag, mpi_Comm comm)
- •Int mpi_Get_count(mpi_Status *status, mpi_Datatype datatype, int *count)
- •Стандартный режим передачи
- •Буферизующий режим передачи
- •1. Барьерная синхронизация - mpi_Barrier (comm)
- •2. Широковещательная передача
- •3. Сбор данных
- •4. Рассылка данных
- •5. Операции редукции (обработки данных)
- •Непрерывный.
- •2. Вектор
- •Int mpi_Type_hvector(int count, int blocklength, int stride, mpi_Datatype oldtype,
- •4. Индексированные данные
- •6. Структурный
- •3. Mpi_Group_translate_ranks (mpi_Group group1, int n, int *ranks1, mpi_Group group2, int *ranks2)
- •4. Mpi_Group _compare(group1, group2, result)
- •6. Mpi_Group _excl(group, n, ranks, newgroup)
- •1. Mpi_Comm_size (comm, size)
- •2. Mpi_Comm_rank(comm, rank)
- •3. Mpi_Comm_compare(comm1, comm2, result)
- •1. Mpi_Comm_dup(comm, newcomm)
- •2. Mpi_Comm_create(comm, group, newcomm)
- •3. Mpi_Comm_split(comm, color, key, newcomm)
- •1. Mpi_Cart_create(mpi_Comm comm_old, int ndims, int *dims, int *periods, int reorder, mpi_Comm *comm_cart)
- •2. Mpi_Dims_create(int nnodes, int ndims, int *dims)
- •1. Mpi_Cartdim_get(mpi_Comm comm, int *ndims)
- •2. Mpi_Cart_get(mpi_Comm comm, int maxdims, int *dims, int *periods, int *coords)
- •3. Int mpi_Cart_rank(mpi_Comm comm, int *coords, int *rank)
- •4. Int mpi_Cart_coords(mpi_Comm comm, int rank, int maxdims, int *coords)
- •5. Координаты декартова сдвига
- •Int mpi_Graph_create(mpi_Comm comm_old, int nnodes, int *index, int *edges, int reorder, mpi_Comm *comm_graph)
- •Int mpi_Topo_test(mpi_Comm comm, int *status)
- •Default(shared | none)
- •Void main()
- •Void main()
- •Int a[10], b[10], c[10]; // целочисленные массивы
- •2. Оператор sections
- •3. Оператор single
- •Void main()
- •10. Функция omp_get_nested
- •Int omp_get_nested(void)
if(скалярное_выражение)
private(список)
firstprivate(список)
Default(shared | none)
shared(список)
copyin(список)
reduction(оператор: список)
1. Если значение выражения в if истинно, область выполняется параллельно, равно нулю (условие не выполняется), то область parallel выполняется последовательно.
2. private(список) - объявляет перечисленные в списке переменные локальными в каждой нити группы. При входе в параллельную область для каждой локальной переменной в каждой нити создается отдельный экземпляр переменной, доступный только этой нити, который не имеет никакой связи с оригинальной переменной вне параллельной области, т.е. при входе и выходе из параллельной области значение переменной не определено, поэтому при входе требуется инициализация. Изменение нитью значения своей локальной переменной, не влияет на изменение значения этой же локальной переменной в других нитях.
3. firstprivate(список) - объявляет перечисленные в списке переменные локальными в каждой нити группы. Локальные копии переменных при входе в параллельную область инициализируются значением оригинальной переменной, при выходе не определены.
4. lastprivate(список) - объявляет перечисленные в списке переменные локальными в каждой нити группы. По окончании параллельно цикла или блока параллельных секций, нить, которая выполнила последнюю итерацию цикла или последнюю секцию блока, обновляет значение оригинальной переменной.
Пример:
………….. // код программы
int i, j;
i =1; j =2;
#pragma omp parallel private(i) firstprivate(j)
{
i =3; // i - private переменная, значение при входе в параллельную область не
// определено, поэтому выполняется инициализация
j =i+j; // j - firstprivate переменная, значение при входе в параллельную область
// определено и равно 2
}
cout<<”i=”<<i<<” j=”<<j<<endl;
………….. // код программы
Переменные i и j при выходе из параллельной области сохранят свои оригинальные значения, т.е. значения до входа в параллельную область i=1, j=2.
Пример:
#pragma omp parallel
{
#pragma omp for lastprivate(i) //значение i не определено при входе,
for (i=0; i<10; i++) // поэтому выполняется инициализация
a[i] = b[i] + 1;
}
a[i-1]=b[i-1];
В значение i в конце параллельной области будет равно 10, как в случае последовательного выполнения цикла.
5. default(shared | none) позволяет установить область видимости переменных. По умолчанию устанавливается shared.
Использование default(none) требует, чтобы для каждой переменной в параллельной секции была явно задана область видимости. В директиве parallel может быть определен единственный оператор default.
Переменные могут быть исключены из умалчиваемого определения путем использования директив private, firstprivate, lastprivate, reduction и shared, например:
#pragma omp parallel for default(shared) firstprivate(i) private(x) private(r) lastprivate(i)
6. shared(список) - объявляет перечисленные в списке переменные общими для всех нитей группы. Т.е. каждая общая переменная существует в одном экземпляре для всей программы и доступна для каждой нити под одним и тем же именем.
Пример: Каждая нить в параллельной области принимает решение, основываясь на номере нити, какую часть массива x обрабатывать.
#pragma omp parallel shared(x, npoints) private(iam, np, ipoints)
{
iam = omp_get_thread_num();
np = omp_get_num_threads();
ipoints = npoints / np;
subdomain(x, iam, ipoints); //функция обработки массива x
}
7. copyin(список) - выполняет присвоение одного и того же значения переменной, определенной в threadprivate в каждую нить параллельной области. Т.е. для каждой переменной, указанной в операторе copyin, значение переменной из основной нити при воде в параллельную область копируется в приватные копии переменных каждой нити. Ограничения, накладываемые на использование клаузы copyin следующее - переменные, указанные в copyin, должны быть переменными threadprivate.
8. reduction(операция: список) - выполняет редукцию переменных, которые перечислены в списке, с оператором op. Список переменных перечисляется через запятую.
Допустимые операции: +, -, *, /, &, |, ||, &&
Ограничения, накладываемые на использование директивы reduction:
1. Тип переменных должен быть приемлемым для операции редукции, не разрешены типы указателя и ссылки.
2.Переменные, которые указаны в клаузе reduction, не должны быть константами.
3.Переменные, которые указаны в клаузе reduction, должны быть разделяемыми в общем контексте программы.
Пример: В данном примере каждая нить имеет свою копию переменной редукции: a и y. После окончания работы выполняется операция редуцирования (в данном случае сложение) всех локальных копий переменных.
#pragma omp parallel for reduction(+: a, y)
for (i=0; i<n; i++) {
a += b[i];
y = sum(y, c[i]);
}
Переменные в операторе reduction должны быть shared в параллельной секции.
.
Пример:
/*ОШИБКА – private переменная y не может быть определена в операторе reduction*/
……. // код программы
#pragma omp parallel for private(y) reduction(+: y)
for (i=0; i<n; i++)
y += b[i];
……. // код программы
Оператор reduction должен выполнить заданную в нем операцию (в данном примере сложение) над значениями указанных переменных (в данном примере y), но переменная y объявлена защищенной (private), а значения защищенных переменных на выходе из параллельного блока не определены. Поэтому, выполнить операцию сложения, заданную в reduction невозможно, а следовательно невозможно выполнить оператор reduction.
Пример:
/*ОШИБКА 2 – переменная x не может быть определена одновременно в операторах shared и reduction */
#pragma omp parallel for shared(x) reduction(+: x)
Пример. Правильное использование reduction
Программа вычисляет и записывает сумму значений элементов массива a в переменную x и сумму значений элементов массива b в переменную y
#include<stdio.h>
#include<omp.h>