Реализация параллельной обработки.

Самая важная и распространенная директива - parallel. Она создает параллельный регион для следующего за ней структурированного блока, например:

#pragma omp parallel [раздел[ [,] раздел]...] структурированный блок

Пример:

pragma omp parallel

{

printf("Hello World\n");

}

Директива parallelсообщает компилятору, что структурированный блок кода должен быть выполнен параллельно, в нескольких потоках. Создается набор (team) из N потоков; исходный поток программы является основным потоком этого набора (master thread) и имеет номер 0. Каждый поток будет выполнять один и тот же поток команд, но не один и тот же набор команд - все зависит от операторов, управляющих логикой программы, таких как if-else.

Директива #pragma omp for сообщает, что при выполнении цикла for в параллельном регионе итерации цикла должны быть распределены между потоками группы. Следует отметить, что в конце параллельного региона выполняется барьерная синхронизация (barrier synchronization). Иначе говоря, достигнув конца региона, все потоки блокируются до тех пор, пока последний поток не завершит свою работу.

Пример:

#pragma omp parallel

{

#pragma omp for

for(int i = 1; i < size; ++i)

x[i] = (y[i-1] + y[i+1])/2;

}

Так как циклы являются самыми распространенными конструкциями, где выполнение кода можно распараллелить, OpenMP поддерживает сокращенный способ записи комбинации директив #pragma omp parallel и #pragma omp for:

#pragma omp parallel for.

При помощи директивы sections выделяется программный код, который далее будет разделен на параллельно выполняемые секции. Директивы section определяют секции, которые могут быть выполнены параллельно.

#pragma omp sections [<параметр> ...]

{

#pragma omp section <блок_программы>

#pragma omp section <блок_программы>

}

При выполнении параллельных фрагментов может оказаться необходимым реализовать часть программного кода только одним потоком (например, открытие файла). Данную возможность в OpenMP обеспечивает директива single.

#pragma omp single [<параметр> ...] <блок_программы>

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

Область видимости переменных:

l Общие переменные (shared) – доступны всем потокам.

l Частные переменные (private) – создаются для каждого потока только на время его выполнения.

Правила видимости переменных:

l все переменные, определенные вне параллельной области – общие;

l все переменные, определенные внутри параллельной области – частные.

Для явного указания области видимости используются следующие параметры директив:

· shared(имя_переменной, …)– общие переменные

· private(имя_переменной, …)– частные переменные

Примеры:

#pragma omp parallel shared(buf)

#pragma omp for private(i, j)

Алгоритмы планировния:

По умолчанию в OpenMP для планирования параллельного выполнения циклов for применяется алгоритм, называемый статическим планированием. Это означает, что все потоки из группы выполняют одинаковое число итераций цикла. Если n - число итераций цикла, а T - число потоков в группе, каждый поток выполнит n/T итераций.

Однако OpenMP поддерживает и другие механизмы планирования, оптимальные в разных ситуациях:

· динамическое планирование (dynamic scheduling);

При динамическом планировании каждый поток выполняет указанное число итераций (по умолчанию равно 1).

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

· планирование в период выполнения (runtime scheduling);

Планирование в период выполнения - это скорее даже не алгоритм планирования, а способ динамического выбора одного из трех описанных алгоритмов.

Если в разделе schedule указан параметр runtime, исполняющая среда OpenMP использует алгоритм планирования, заданный для конкретного цикла for при помощи переменной OMP_SCHEDULE.

Переменная OMP_SCHEDULE имеет формат «тип[,число итераций]», например:

set OMP_SCHEDULE=dynamic,8

Планирование в период выполнения дает определенную гибкость в выборе типа планирования, при этом по умолчанию применяется статическое планирование.

· управляемое планирование (guided scheduling);

При управляемом планировании число итераций, выполняемых каждым потоком, определяется по следующей формуле:

число_выполняемых_потоком_итераций = max(число_нераспределенных_итераций/omp_get_num_threads(), число итераций

· автоматическое планирование (OpenMP 3.0) (auto).

Способ распределения итераций цикла между потоками определяется реализацией компилятора.

На этапе компиляции программы или во время ее выполнения определяется оптимальный способ распределения.

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

Наши рекомендации