OpenMP¶
OpenMP (Open Multi-Processing) — это открытый стандарт для параллельного программирования на языках C, C++ и Fortran, реализованный в виде набора директив компилятора, библиотечных функций и переменных окружения. OpenMP предназначен для создания многопоточных приложений на системах с общей памятью (SMP, NUMA), позволяя разработчику постепенно распараллеливать последовательный код, добавляя в него специальные указания (прагмы). Стандарт поддерживается большинством современных компиляторов, включая GCC, Clang, Intel C++ Compiler, MSVC и другие.
¶История
Разработка OpenMP началась в 1997 году как консорциум ведущих производителей вычислительной техники и программного обеспечения, включая компанию SGI, Sun Microsystems, Intel, IBM и другие. Первая версия спецификации (OpenMP 1.0) для языков Fortran была выпущена в октябре 1997 года, для C и C++ — в 1998 году. Основной целью было создание переносимого, простого в использовании стандарта, который бы позволил разработчикам легко распараллеливать существующие последовательные программы без радикального переписывания кода.
В 2000 году вышла версия 2.0, которая расширила возможности управления данными и добавила поддержку вложенного параллелизма. Версия 3.0 (2008 год) стала важным этапом: в неё были включены задачи (tasks), что позволило эффективно распараллеливать рекурсивные алгоритмы и нерегулярные структуры данных. Версия 4.0 (2013 год) добавила поддержку векторизации (SIMD), ускорителей (акселераторов) и расширила модель управления данными. Версия 4.5 (2015 год) улучшила поддержку гетерогенных вычислений. Версия 5.0 (2018 год) привнесла поддержку параллелизма на уровне памяти (memory allocators), улучшенную обработку ошибок и расширенные возможности для работы с GPU. Текущая версия, OpenMP 5.2 (2021 год), продолжает развитие в направлении поддержки новых архитектур и упрощения программирования для гетерогенных систем.
¶Модель параллелизма
OpenMP основан на модели fork-join (разветвление-слияние). Программа начинается как единственный поток (главный, master thread). При входе в параллельную область (например, директивой #pragma omp parallel) создаётся группа потоков (team of threads). Главный поток становится мастером группы, а остальные потоки — рабочими. Каждый поток выполняет код параллельной области. После выхода из параллельной области потоки синхронизируются, и выполнение продолжается только главным потоком.
¶Управление потоками
Количество потоков в параллельной области может задаваться:
- Переменной окружения
OMP_NUM_THREADS. - Функцией
omp_set_num_threads(). - Директивой
num_threads(N)в#pragma omp parallel.
По умолчанию количество потоков равно числу доступных ядер процессора (или логических процессоров, если включён Hyper-Threading).
¶Переменные окружения
OpenMP предоставляет ряд переменных окружения для управления поведением программы:
OMP_NUM_THREADS— задаёт максимальное количество потоков.OMP_SCHEDULE— определяет стратегию распределения итераций цикла.OMP_DYNAMIC— разрешает или запрещает динамическое изменение числа потоков.OMP_NESTED— включает или отключает вложенный параллелизм.OMP_STACKSIZE— задаёт размер стека для каждого потока.
¶Директивы компилятора
Директивы OpenMP в C/C++ оформляются как #pragma omp .... В Fortran они имеют вид !$omp .... Основные директивы:
¶Параллельная область
pragma omp parallel — создаёт группу потоков, каждый из которых выполняет следующий блок кода.
```c
¶pragma omp parallel
{ printf("Привет от потока %d\n", omp_get_thread_num()); } ```
¶Распределение работы
#pragma omp for— распределяет итерации цикла между потоками. Должна быть внутри параллельной области.#pragma omp sections— разделяет блоки кода на независимые секции, каждая из которых выполняется одним потоком.#pragma omp single— указывает, что следующий блок должен выполнить только один поток (обычно первый).#pragma omp task— создаёт задачу, которая может быть выполнена любым потоком в группе. Позволяет эффективно распараллеливать рекурсивные алгоритмы (например, обход дерева, быстрая сортировка).
¶Синхронизация
#pragma omp barrier— точка синхронизации: все потоки должны достичь этой точки, прежде чем любой из них продолжит выполнение.#pragma omp critical— определяет критическую секцию, которая может выполняться только одним потоком в любой момент времени.#pragma omp atomic— выполняет атомарную операцию над переменной (например, чтение-модификация-запись).#pragma omp ordered— гарантирует, что итерации цикла будут выполняться в порядке, соответствующем последовательному выполнению.#pragma omp master— указывает, что блок должен выполнить только главный поток.
¶Управление данными
private(var)— каждый поток получает свою копию переменнойvar, начальное значение не определено.firstprivate(var)— какprivate, но копия инициализируется значением из главного потока.lastprivate(var)— какprivate, но после завершения параллельной области значение из последней итерации (или последней секции) копируется обратно в главный поток.shared(var)— переменная является общей для всех потоков (по умолчанию).default(none)— требует явного указания для всех переменных, используемых в параллельной области.reduction(operator:var)— выполняет редукцию (суммирование, произведение, максимум, минимум и т.д.) над переменнойvarпо всем потокам.
¶Редукция
Редукция — одна из ключевых возможностей OpenMP. Она позволяет безопасно выполнять операции, требующие глобального накопления результата (например, вычисление суммы элементов массива). Для каждого потока создаётся локальная копия переменной, после завершения параллельной области все локальные копии объединяются с помощью заданной операции.
```c int sum = 0;
¶pragma omp parallel for reduction(+:sum)
for (int i = 0; i < N; i++) { sum += array[i]; } ```
¶Библиотечные функции
OpenMP предоставляет набор функций, объявленных в заголовочном файле <omp.h> (для C/C++) или модуле omp_lib (для Fortran). Основные функции:
omp_get_thread_num()— возвращает номер текущего потока (от 0 доomp_get_num_threads()-1).omp_get_num_threads()— возвращает количество потоков в текущей параллельной области.omp_get_max_threads()— возвращает максимальное количество потоков, которое может быть создано.omp_in_parallel()— возвращает 1, если текущий код выполняется в параллельной области, иначе 0.omp_set_num_threads(int n)— устанавливает количество потоков для последующих параллельных областей.omp_get_wtime()— возвращает текущее время в секундах (для измерения производительности).omp_get_thread_limit()— возвращает максимальное количество потоков, поддерживаемое системой.
¶Примеры использования
¶Параллельный цикл с редукцией
```c
¶include <stdio.h>
¶include <omp.h>
int main() { int N = 1000000; double sum = 0.0; double a = (double)malloc(N * sizeof(double)); // инициализация массива for (int i = 0; i < N; i++) a[i] = 1.0 / (i + 1);
¶pragma omp parallel for reduction(+:sum)
for (int i = 0; i < N; i++) { sum += a[i]; } printf("Сумма: %f\n", sum); free(a); return 0; } ```
¶Использование задач (tasks)
```c
¶include <stdio.h>
¶include <omp.h>
void compute(int n) { printf("Задача %d выполняется потоком %d\n", n, omp_get_thread_num()); }
int main() {
¶pragma omp parallel
{
¶pragma omp single
{ for (int i = 0; i < 10; i++) {
¶pragma omp task
compute(i); } } } return 0; } ```
¶Критика и ограничения
OpenMP критикуется за:
- Ориентацию на общую память: модель не подходит для распределённых систем (кластеры, суперкомпьютеры с распределённой памятью), где требуется MPI.
- Сложность отладки: многопоточные программы с OpenMP могут содержать трудноуловимые ошибки синхронизации (гонки данных, взаимные блокировки).
- Производительность: накладные расходы на создание и синхронизацию потоков могут быть значительными для мелкозернистого параллелизма.
- Неполная поддержка: некоторые компиляторы реализуют не все возможности стандарта, особенно в старых версиях.
- Зависимость от компилятора: хотя стандарт переносим, производительность может сильно варьироваться в зависимости от реализации.
¶Применение
OpenMP широко применяется в научных и инженерных расчётах, обработке изображений, численном моделировании, машинном обучении и других областях, где требуется ускорение вычислений на многоядерных процессорах. Он часто используется в комбинации с MPI для гибридного параллелизма (MPI+OpenMP) на кластерных системах. OpenMP также поддерживается в некоторых GPU-ускорителях (через директивы target), что позволяет писать программы, работающие как на CPU, так и на GPU.
¶Источники
- OpenMP Application Programming Interface, Version 5.2 — спецификация стандарта OpenMP, 2021.
- Chandra, R., Dagum, L., Kohr, D., Maydan, D., McDonald, J., Menon, R. — Parallel Programming in OpenMP, Morgan Kaufmann, 2001.
- Quinn, M. J. — Parallel Programming in C with MPI and OpenMP, McGraw-Hill, 2003.
- Chapman, B., Jost, G., van der Pas, R. — Using OpenMP: Portable Shared Memory Parallel Programming, MIT Press, 2007.
- Документация GCC по OpenMP — gcc.gnu.org/onlinedocs/libgomp/.
- Документация Intel C++ Compiler по OpenMP — software.intel.com.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →

