Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции_MPI_OpenMP.doc
Скачиваний:
4
Добавлен:
16.11.2019
Размер:
1.09 Mб
Скачать

Void main()

{

int i, max;

int num_threads=2;

int a[10];

for (i = 0; i < 10; i++) {

a[i] = rand()/1000;

printf("%d\n", a[i]);

}

max = a[0];

omp_set_num_threads(num_threads);

#pragma omp parallel for private(i)

for (i = 1; i < 10; i++)

{

if (a[i] > max)

{

#pragma omp critical // если нашли новый максимум, корректируем значение общей переменой max

if (a[i] > max)

max = a[i];

printf("Thread %d max=%d\n", omp_get_thread_num(), max);

}

}

printf("max = %d\n", max);

}

3. atomic гарантирует, что специфическая ячейка памяти обновляется атомарно (элементарно), в отличие от возможного обновления её несколькими, одновременно пишущими нитями. Данная директива относится к идущему непосредственно за ней оператору, гарантируя корректную работу с общей переменной, стоящей в левой части конструкции оператор-выражение, для предыдущего примера SUM=SUM+Expr это переменная SUM, стоящая в левой части присваивания. Для исключения условия «гонки» (race condition) все модификации ячеек в параллельном режиме должны быть защищены директивой atomic.

Синтаксис директивы atomic:

#pragma omp atomic

оператор-выражение

, где оператор-выражение должен иметь одну из следующих форм:

  • x binop = выражение

  • x++

  • ++x

  • x—

  • --x

В предыдущих выражениях:

x - переменная.

выражение - должно быть выражением скалярного типа.

binop - должен быть одним из следующих операторов: +, *, -, /, &, |, <<, или >>.

По сути atomic и critical решают одну и ту же проблему, но директива atomic более эффективна, чем critical.

Пример: Следующий пример исключает условие «гонки», т.е. одновременное изменение элемента x несколькими нитями, путем использования директивы atomic:

#pragma omp parallel for shared(x, y, n)

for (i=0; i<n; i++) {

#pragma omp atomic

x[i] += work1(i);

y[i] += work2(i);

}

Преимущество использования директивы atomic в этом примере состоит в том, что директива позволяет изменить значения двух различных элементов x параллельно. Если вместо директивы atomic была бы использована директива critical, то все модификации элементов x должны были быть выполнены последовательно (хотя и в не гарантированном порядке).

Заметим, что директива atomic приемлема только к оператору, непосредственно следующему за директивой. И как следствие, в этом примере, элемент y не модифицируется атомарно.

4. barrier выполняет явную синхронизацию работы всех нитей в группе. При достижении оператора barrier, каждая нить останавливается и ожидает, пока остальные нити достигнут этой точки, после чего все нити продолжают работать дальше в параллельном режиме.

Синтаксис директивы barrier:

#pragma omp barrier

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

flush определяет пересечение нитей (cross-thread) в точке оценки, где требуется гарантия того, что все нити в группе имеют одно и то же видение некоторых объектов памяти. Это означает, что предыдущие вычисления выражений, которые ссылаются на эти объекты памяти, были завершены, а последующие вычисления не начаты.

Синтаксис директивы flush:

#pragma omp flush [(список)]

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

Директива flush без списка синхронизирует все разделяемые переменные (такой способ синхронизации будет иметь значительные накладные расходы по сравнению с директивой flush со списком). Директива flush без списка может использоваться в директивах:

  • barrier.

  • При ходе или выходе из конструкции critical.

  • При ходе или выходе из конструкции ordered.

  • При выходе из конструкции parallel.

  • При выходе из конструкции for.

  • При выходе из конструкции sections.

  • При выходе из конструкции single.

Директиву flush нельзя использовать, если присутствует оператор nowait (т.е. выполнение без синхронизации).

Ограничения к использованию директивы flush:

1. Переменная, определенная в директиве flush, не должна быть ссылкой.

2. программная конструкция, которая содержит директиву barrier, должна быть блоком (или составным оператором).

6. ordered - директива должна находиться внутри конструкций for или parallel for. Программный блок, непосредственно следующий за директивой ordered, выполняется в порядке, в котором итерации цикла выполнялись бы в последовательном режиме.

Синтаксис директивы ordered:

#pragma omp ordered

структурированный блок

Подробно директива ordered рассматривалась выше в теме разделения работ с конструкцией for.

Переменные окружения OpenMP

Переменные окружения управляют выполнением параллельного кода. Механизм присвоения значений переменным окружения определяется правилами ОС. Изменение значений после начала выполнения программы игнорируются.

Переменные среды:

  • OMP_SCHEDULE устанавливает способ планирования и размер порции при выполнении циклов,

  • OMP_NUM_THREADS устанавливает число порождаемых при выполнении программы нитей,

  • OMP_DYNAMIC разрешает или запрещает динамическое регулирование числом нитей,

  • OMP_NESTED разрешает или запрещает вложенный параллелизм.

1. OMP_SCHEDULE - применяется только к директивам for и рассмотрен выше в теме разделения работ.

2.OMP_NUM_THREADS - значение должно быть положительным. Действие переменной зависит от того, допускается ли динамическое регулирование числа нитей.

Если динамическое регулирование запрещено, то значение переменной равно числу нитей, определенных на момент начала выполнения программы, пока это число явным образом не изменится в течение выполнения программы путем вызова функции omp_set_num_threads().

Если динамическое регулирование числа нитей разрешено, значение переменной среды рассматривается как максимальное число нитей, доступных для использования. Значение по умолчанию зависит от реализации OpenMP.

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

Пример: export OMP_NUM_THREADS=16

3. OMP_DYNAMIC - разрешает или запрещает динамическое регулирование количества порождаемых нитей для выполнения параллельных блоков программы. Значение переменной может быть TRUE или FALSE.

Если значение переменной TRUE, то число нитей, используемых для выполнения параллельной области, может регулироваться для оптимального использования ресурсов системы.

Если значение переменной FALSE, динамическое регулирование запрещено. Значение по умолчанию зависит от реализации OpenMP.

Пример: export OMP_DYNAMIC = TRUE

4. OMP_NESTED - разрешает или запрещает вложенный параллелизм. Если значение переменной TRUE - вложенный параллелизм разрешен; если FALSE - вложенный параллелизм запрещен. По умолчанию - FALSE.

Пример: export OMP_NESTED = TRUE

Стандартные функции OpenMP

Прототипы функций OpenMP находятся в файле <omp.h>, используются для управления состоянием среды выполнения параллельной программы и синхронизации работы потоков.

  • omp_set_num_threads.

  • omp_get_num_threads.

  • omp_get_max_threads.

  • omp_get_thread_num.

  • omp_get_num_procs.

  • omp_in_parallel.

  • omp_set_dynamic.

  • omp_get_dynamic.

  • omp_set_nested.

  • omp_get_nested

1. Функция omp_set_num_threads

Устанавливает число нитей для выполнения параллельной области.

void omp_set_num_threads(int num_threads)

Значение параметра num_threads должно быть положительным. Эта функция имеет приоритет перед переменной окружения OMP_NUM_THREADS.

2. Функция omp_get_num_threads

Возвращает число нитей в группе выполняющих параллельную работу на момент обращения к функции (аналог MPI_Comm_size(…)).

int omp_get_num_threads(void)

Если число нитей явным образом не установлено пользователем (ф-ция omp_set_num_threads или пер-ная OMP_NUM_THREADS), то по умолчанию устанавливается значение, определяемое конкретной реализацией. Эта функция возвращает значение числа нитей ближайшей к вызову параллельной области программы. Если вызов осуществляется из последовательной части программы или вложенной параллельной области, которая выполняется последовательно, то функция возвращает значение равное 1.

3. Функция omp_get_max_threads

Возвращает максимальное значение, которое может быть возвращено функцией omp_get_num_threads на момент вызова omp_get_max_threads.

int omp_get_max_threads(void)

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

4. Функция omp_get_thread_num

Возвращает номер нити, которая вызвала эту функцию в группе. Номер нити располагается между 0 и значением функции omp_get_num_threads()–1 включительно. Основная нить группы всегда имеет номер 0.

int omp_get_thread_num(void);

Если функция вызывается из последовательной области, то вызов функции omp_get_num_threads() возвращает 0.

Мы уже говорили о том, что все порожденные нити исполняют один и тот же код. Используя функцию omp_get_thread_num можно распределить работу между нитями. Например:

if (omp_get_thread_num() == 3 )

< код для нити с номером 3 >

else < код для всех остальных нитей >

часть программы в if будет выполнена только нитью с номером 3, а часть else - всеми остальными.

5. Функция omp_get_num_procs

Возвращает максимальное число процессоров, которое может быть присвоено программе.

int omp_get_num_procs(void)

6. Функция omp_in_parallel

Возвращает ненулевое значение, если вызов выполнен в параллельной области, в противном случае, возвращает 0.

int omp_in_parallel(void)

7. Функция omp_set_dynamic

Разрешает или запрещает динамическое изменение числа нитей для выполнения параллельной области.

void omp_set_dynamic(int dynamic_threads)

Функция действительна, если она вызывается в последовательной области, в противном случае поведение функции неопределенно. Если переменная dynamic_threads равна 0, то динамическое регулирование запрещено. Вызов функции omp_set_dynamic имеет приоритет перед переменной окружения OMP_DYNAMIC.

Значение по умолчанию зависит от реализации OpenMP. Если код параллельной программы зависит от определенного числа нитей, то для правильного выполнения, необходимо явно запретить динамическое изменение числа нитей.

8. Функция omp_get_dynamic

Возвращает ненулевое значение, если динамическое изменение числа нитей разрешено, в противном случае возвращает 0.

int omp_get_dynamic(void)

9. Функция omp_set_nested

Разрешает или запрещает вложенный параллелизм.

void omp_set_nested(int nested)

Если значение переменной nested равно 0, значение по умолчанию, вложенный параллелизм запрещен, а вложенная параллельная область выполняется последовательно текущей нитью. Если переменная nested имеет ненулевое значение, вложенный параллелизм разрешен, и параллельная область в начале выполнения порождает определенное число нитей. Функция имеет приоритет перед переменной OMP_NESTED.

Стандарт OpenMP разрешает выполнять вложенные параллельные области последовательно, даже если вложенный параллелизм разрешен.