поддерживаю выше заданые вопросы
|
Введение в технологии параллельного программирования (OpenMP)
4.7.2. Выполнение барьерной синхронизации (директива barrier)
При помощи директивы barrier можно определить точку синхронизации, которую должны достигнуть все потоки для продолжения вычислений (директива может находиться в пределах как параллельного фрагмента, так и параллельной области, т. е. директива является отделяемой). Формат директивы barrier имеет вид:
#pragma omp barrier
4.7.3. Синхронизация состояния памяти (директива flush)
Директива flush позволяет определить точку синхронизации, в которой системой должно быть обеспечено единое для всех потоков состояние памяти (т. е. если потоком какое-либо значение извлекалось из памяти для модификации, то измененное значение обязательно должно быть записано в общую память).
Формат директивы flush имеет вид:
#pragma omp flush [(list)]
Как показывает формат, директива содержит список list с перечнем переменных, для которых выполняется синхронизация; при отсутствии списка синхронизация выполняется для всех переменных потока.
Следует отметить, что директива flush неявным образом присутствует в директивах barrier, critical, ordered, parallel, for, sections, single.
4.7.4. Определение постоянных локальных переменных потоков (директива threadprivate и параметр copyin директивы parallel)
Как описывалось при рассмотрении директивы parallel, для потоков могут быть определены локальные переменные (при помощи параметров private, firstprivate, lastprivate), которые создаются в начале соответствующего параллельного фрагмента и удаляются при завершении потоков. В OpenMP имеется возможность создания и постоянно существующих локальных переменных для потоков при помощи директивы threadprivate.
Формат директивы threadprivate имеет вид:
#pragma omp threadprivate (list)
Список list содержит набор определяемых переменных. Созданные локальные копии не видимы в последовательных участках выполнения программы (т. е. вне параллельных фрагментов), но существуют в течение всего времени выполнения программы. Указываемые в списке переменные должны быть уже определены в программе; объявление переменных в директиве должно предшествовать использованию переменных в потоках.
Следует отметить, что использование директивы threadprivate позволяет решить еще одну проблему. Дело в том, что действие параметров private распространяется только на программный код параллельных фрагментов, но не параллельных областей – т. е., например, любая локальная переменная, определенная в параллельном фрагменте функции root на рис. 4.3, будет недоступна в параллельной области функции node. Выход из такой ситуации может быть или в передаче значений локальных переменных через параметры функций, или же в использовании постоянных локальных переменных директивы threadprivate.
Отметим еще раз, что полученные в потоках значения постоянных переменных сохраняются между параллельными фрагментами программы. Значения этих переменных можно переустановить при начале параллельного фрагмента по значениям из основного потока при помощи параметра copyin директивы parallel.
Формат параметра copyin директивы parallel имеет вид:
copyin (list)
Для демонстрации рассмотренных понятий приведем пример A.32.1c из стандарта OpenMP 2.5.
#include <stdlib.h> float* work; int size; float tol; #pragma omp threadprivate(work,size,tol) void a32( float t, int n ) { tol = t; size = n; #pragma omp parallel copyin(tol,size) { build(); } } void build() { int i; work = (float*)malloc( sizeof(float)*size ); for( i = 0; i < size; ++i ) work[i] = tol; }
В приведенном примере определяются постоянно существующие локальные переменные work, size, tol для потоков (директива threadprivate). Перед началом параллельного фрагмента значения этих переменных из основного потока копируются во все потоки (параметр copyin). Значения постоянно существующих локальных переменных доступны в параллельной области функции build (для обычных локальных переменных потоков их значения пришлось бы передавать через параметры функции).
4.7.5. Управление количеством потоков
По умолчанию количество создаваемых потоков определяется реализацией и обычно совпадает с числом имеющихся вычислительных элементов в системе (процессоров и/или ядер) 4Определение количества имеющихся вычислительных элементов осуществляется операционной системой. При этом следует понимать, что при аппаратной поддержке процессором технологии многопоточности (hyperthreading, HT) ОС будет воспринимать каждый процессор как несколько логических вычислительных элементов (по числу аппаратно поддерживаемых потоков). . При необходимости количество создаваемых потоков может быть изменено – для этой цели в OpenMP предусмотрено несколько способов действий.
Прежде всего, количество создаваемых потоков может быть задано при помощи параметра num_threads директивы parallel.
Количество необходимых потоков может быть также задано при помощи функции omp_set_num_threads библиотеки OpenMP.
Формат функции omp_set_num_threads имеет вид:
void omp_set_num_threads (int num_threads);
Вызов функции omp_set_num_threads должен осуществляться из последовательной части программы.
Для задания необходимого количества потоков можно воспользоваться и переменной окружения OMP_NUM_THREADS. При использовании нескольких способов задания наибольший приоритет имеет параметр num_threads директивы parallel, затем функция библиотеки, затем переменная окружения.
Изменение количества создаваемых потоков может быть полезно и для целей отладки разрабатываемой параллельной программы с целью проверки ее работоспособности при разном количестве потоков.
Приведем здесь также дополнительные функции библиотеки OpenMP, которые могут быть использованы при работе с потоками:
- Получение максимально-возможного количества потоков:
int omp_get_max_threads(void)
- Получение фактического количества потоков в параллельной области программы:
int omp_get_num_threads(void)
- Получение номера потока:
int omp_get_thread_num(void)
- Получение числа вычислительных элементов (процессоров или ядер), доступных приложению:
int omp_get_num_procs(void)
4.7.6. Задание динамического режима при создании потоков
Количество создаваемых потоков в параллельных фрагментах программы по умолчанию является фиксированным (см. также предыдущий пункт), однако стандартом предусматривается возможность динамического режима, когда количество потоков может определяться реализацией для оптимизации функционирования вычислительной системы. Разрешение динамического режима и его отключение осуществляется при помощи функции omp_set_dynamic библиотеки OpenMP.
Формат функции omp_set_ dynamic имеет вид:
void omp_set_dynamic (int dynamic);
Вызов функции omp_set_dynamic должен осуществляться из последовательной части программы. Функция разрешает (dynamic=true) или отключает (dynamic=false) динамический режим создания потоков.
Для управления динамическим режимом создания потоков можно воспользоваться и переменной окружения OMP_DYNAMIC. При использовании нескольких способов задания наибольший приоритет имеет функция библиотеки, затем переменная окружения.
Для получения состояния динамического режима можно воспользоваться функций omp_get_dynamic библиотеки OpenMP:
int omp_get_dynamic (void);
4.7.7. Управление вложенностью параллельных фрагментов
Параллельные фрагменты программы могут быть вложенными – такая возможность определяется реализацией OpenMP. По умолчанию для выполнения вложенных параллельных фрагментов создается столько же потоков, как и для параллельных фрагментов верхнего уровня.
Управление режимом выполнения вложенных фрагментов осуществляется при помощи функции omp_set_nested библиотеки OpenMP.
Формат функции omp_set_nested имеет вид:
void omp_set_nested (int nested);
Вызов функции omp_set_nested должен осуществляться из последовательной части программы. Функция разрешает (nested=true) или отключает (nested=false) режим поддержки вложенных параллельных фрагментов.
Для управления режимом поддержки вложенных параллельных фрагментов можно воспользоваться и переменной окружения OMP_NESTED. При использовании нескольких способов задания наибольший приоритет имеет функция библиотеки, затем переменная окружения.
Для получения состояния режима поддержки вложенных параллельных фрагментов можно воспользоваться функций omp_get_nested библиотеки OpenMP:
int omp_get_nested (void);