ТЕХНОЛОГИИ 

Что такое OpenMP?

Содержание документа

1. Введение. Основные идеи, причина возникновения, модель программирования. Для чего создан новый интерфейс? Кто разрабатывает? Что входит в стандарт? Сравнение с MPI и другими средствами. POSIX threads и основные понятия, связанные с нитями. Механизм работы - директивы (спецкомментарии), инкрементальное распараллеливание. Пример - параллельное вычисление числа Пи.

Примечание. В разделах 2-4 не ставится целью точно определить синтаксис OpenMP, но дается общее представление о назначении и функциональности директив и процедур OpenMP. За точным определение синтаксиса, обращайтесь, пожалуйста к спецификациям.

2. Основные конструкции (директивы). PARALLEL - порождение нитей. DO/SECTIONS - распределение работы; распределение итераций в циклах. Средства синхронизации.

3. Классы переменных (PRIVATE,SHARED,REDUCTION, etc.)

4. Run-time-поддержка. Процедуры, переменные среды,.

5. Привязки к C/C++.

6. Поддержка производителями и разработчиками. Реализации. Компиляторы и препроцессоры. Приложения. Будущее развитие, место среди средств параллельного программирования. Конференции.

7. Справочная информация. Документы для загрузки. Ссылки.


1. Введение

Что такое OpenMP?
Интерфейс OpenMP задуман как стандарт для программирования на масштабируемых SMP-системах (SSMP,ccNUMA, etc.) в модели общей памяти (shared memory model). В стандарт OpenMP входят спецификации набора директив компилятора, процедур и переменных среды.

Примерами систем с общей памятью, масштабируемых до большого числа процессоров, могут служить суперкомпьютеры Cray Origin2000 (до 128 процессоров), HP 9000 V-class (до 32 процессоров в одном узле, а в конфигурации из 4 узлов - до 128 процессоров), Sun Starfire (до 64 процессоров).


Кто разрабатывает стандарт?
Разработкой стандарта занимается организация OpenMP ARB (ARchitecture Board), в которую вошли представители крупнейших компаний - разработчиков SMP-архитектур и программного обеспечения. Спецификации для языков Fortran и C/C++ появились соответственно в октябре 1997 года и октябре 1998 года. Открыт список рассылки для публичного обсуждения OpenMP (omp@openmp.org).

Где найти информацию?
Основной источник информации - сервер www.openmp.org. На сервере доступны спецификации, статьи, учебные материалы, ссылки.

Зачем нужен новый стандарт?
До появления OpenMP не было подходящего стандарта для эффективного программирования на SMP-системах.

Наиболее гибким, переносимым и общепринятым интерфейсом параллельного программирования является MPI (интерфейс передачи сообщений). Однако модель передачи сообщений 1) недостаточно эффективна на SMP-системах; 2) относительно сложна в освоении, так как требует мышления в "невычислительных" терминах.

Проект стандарта X3H5 провалился, так как был предложен во время всеобщего интереса к MPP-системам, а также из-за того, что в нем поддерживается только параллелизм на уровне циклов. OpenMP развивает многие идеи X3H5.

POSIX-интерфейс для организации нитей (Pthreads) поддерживается широко (практически на всех UNIX-системах), однако по многим причинам не подходит для практического параллельного программирования:

  1. нет поддержки Fortran-а,
  2. слишком низкий уровень,
  3. нет поддержки параллелизма по данным,
  4. механизм нитей изначально разрабатывался не для целей организации параллелизма.
OpenMP можно рассматривать как высокоуровневую надстройку над Pthreads (или аналогичными библиотеками нитей).

Многие поставщики SMP-архитектур (Sun,HP,SGI) в своих компиляторах поддерживают спецдирективы для распараллеливания циклов. Однако эти наборы директив, как правило, 1) весьма ограничены; 2) несовместимы между собой; в результате чего разработчикам приходится распараллеливать приложение отдельно для каждой платформы. OpenMP является во многом обобщением и расширением упомянутых наборов директив.


Какие преимущества OpenMP дает разработчику?

1. За счет идеи "инкрементального распараллеливания" OpenMP идеально подходит для разработчиков, желающих быстро распараллелить свои вычислительные программы с большими параллельными циклами. Разработчик не создает новую параллельную программу, а просто последовательно добавляет в текст последовательной программы OpenMP-директивы.

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

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

4. Одним из достоинств OpenMP его разработчики считают поддержку так называемых "orphan" (оторванных) директив, то есть директивы синхронизации и распределения работы могут не входить непосредственно в лексический контекст параллельной области.


Как это работает?
Согласно терминологии POSIX threads, любой UNIX-процесс состоит несколько нитей управления, которые имеют общее адресное пространство, но разные потоки команд и раздельные стэки. В простейшем случае, процесс состоит из одной нити. Нити иногда называют также потоками, легковесными процессами, LWP (light-weight processes).


Подробнее о механизме нитей:

В OpenMP используется терминология и модель программирования, близкая к Pthreads (динамически порождаемые нити, общие и разделяемые данные, механизм "замков" для синхронизации). Предполагается наиболее вероятным, что OpenMP будет реализован на базе Pthreads.

Как это выглядит?
Простой пример: вычисление числа "Пи". В последовательную программу вставлены две строчки, и она распараллелена!

program compute_pi
   parameter (n = 1000)
   integer i
   double precision w,x,sum,pi,f,a
   f(a) = 4.d0/(1.d0+a*a)
   w = 1.0d0/n
   sum = 0.0d0;
!$OMP PARALLEL DO PRIVATE(x), SHARED(w)
!$OMP& REDUCTION(+:sum)

   do i=1,n
       x = w*(i-0.5d0)
       sum = sum + f(x)
   enddo

   pi = w*sum
   print *,'pi = ',pi
   stop
   end

2. Директивы

Директивы OpenMP с точки зрения Фортрана являются комментариями и начинаются с комбинации символов "!$OMP". Директивы можно разделить на 3 категории: определение параллельной секции, разделение работы, синхронизация. Каждая директива может иметь несколько дополнительных атрибутов - клауз. Отдельно специфицируются клаузы для назначения классов переменных, которые могут быть атрибутами различных директив.

Порождение нитей

PARALLEL ... END PARALLEL
Определяет параллельную область программы. При входе в эту область порождаются новые (N-1), образуется "команда" из N нитей, а порождающая нить получает номер 0 и становится основной нитью команды (т.н. "master thread"). При выходе из параллельной области основная нить дожидается завершения остальных нитей, и продолжает выполнение в одном экземпляре. Предполагается, что в SMP-системе нити будут распределены по различным процессорам (однако это, как правило, находится в ведении операционной системы).

Каким образом между порожденными нитями распределяется работа - определяется директивами DO,SECTIONS и SINGLE. Возможно также явное управление распределением работы (а-ля MPI) с помощью функций, возвращающих номер текущей нити и общее число нитей. По умолчанию (вне этих директив), код внутри PARALLEL исполняется всеми нитями одинаково.

Вместе с PARALLEL может использоваться клауза IF(условие) - й параллельная работа инициируется только при выполнении указанного в ней условия.

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


Разделение работы (work-sharing constructs)

Параллельные циклы

DO ... [ENDDO]
Определяет параллельный цикл.

Клауза SCHEDULE определяет способ распределения итераций по нитям:

По умолчанию, в конце цикла происходит неявная синхронизация; эту синхронизацию можно запретить с помощью ENDDO NOWAIT.

Параллельные секции

SECTIONS ... END SECTIONS
Не-итеративная параллельная конструкция. Определяет набор независимых секций кода (т.н., "конечный" параллелизм). Секции отделяются друг от друга директивой SECTION.

Примечание. Если внутри PARALLEL содержится только одна конструкция DO или только одна конструкия SECTIONS, то можно использовать укороченную запись: PARALLEL DO или PARALLEL SECTIONS.

Исполнение одной нитью

SINGLE ... END SINGLE
Определяет блок кода, который будет исполнен только одной нитью (первой, которая дойдет до этого блока).

Явное управление распределением работы

С помощью функций OMP_GET_THREAD_NUM() и OMP_GET_NUM_THREADS нить может узнать свой номер и общее число нитей, а затем выполнять свою часть работы в зависимости от своего номера (этот подход широко используется в программах на базе интерфейса MPI).


Директивы синхронизации

MASTER ... END MASTER
Определяет блок кода, который будет выполнен только master-ом (нулевой нитью).

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

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

ATOMIC
Определяет переменную в левой части оператора "атомарного" присваивания, которая должна корректно обновляться несколькими нитями.

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

FLUSH
Явно определяет точку, в которой реализация должна обеспечить одинаковый вид памяти для всех нитей. Неявно FLUSH присутствует в следующих директивах: BARRIER, CRITICAL, END CRITICAL, END DO, END PARALLEL, END SECTIONS, END SINGLE, ORDERED, END ORDERED.

В целях синхронизации можно также пользоваться механизмом замков (locks).


3. Классы переменных

В OpenMP переменные в параллельных областях программы разделяются на два основных класса:

Отдельные правила определяют поведение переменных при входе и выходе из параллельной области или параллельного цикла: REDUCTION, FIRSTPRIVATE, LASTPRIVATE, COPYIN.

По умолчанию, все COMMON-блоки, а также переменные, порожденные вне параллельной области, при входе в эту область остаются общими (SHARED). Исключение составляют переменные - счетчики итераций в цикле, по очевидным причинам. Переменные, порожденные внутри параллельной области, являются приватными (PRIVATE). Явно назначить класс переменных по умолчанию можно с помощью клаузы DEFAULT.

SHARED
Применяется к переменным, которые необходимо сделать общими.

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

THREADPRIVATE
Применяется к COMMON-блокам, которые необходимо сделать приватными. Директива должна применяться после каждой декларации COMMON-блока.

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

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

REDUCTION(+:A)
Обозначает переменную, с которой в цикле производится reduction-операция (например, суммирование). При выходе из цикла, данная операция производится над копиями переменной во всех нитях, и результат присваивается оригинальной переменной.

COPYIN
Применяется к COMMON-блокам, которые помечены как THREADPRIVATE. При входе в параллельную область приватные копии этих данных инициализируются оригинальными значениями.


4. Runtime-процедуры и переменные среды

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

В OpenMP предусмотрен также набор библиотечных процедур, которые позволяют:

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

OMP_SCHEDULE
Определяет способ распределения итераций в цикле, если в директиве DO использована клауза SCHEDULE(RUNTIME).

OMP_NUM_THREADS
Определяет число нитей для исполнения параллельных областей приложения.

OMP_DYNAMIC
Разрешает или запрещает динамическое изменение числа нитей.

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


Процедуры для контроля/запроса параметров среды исполнения

OMP_SET_NUM_THREADS
Позволяет назначить максимальное число нитей для использования в следующей параллельной области (если это число разрешено менять динамически). Вызывается из последовательной области программы.

OMP_GET_MAX_THREADS
Возвращает максимальное число нитей.

OMP_GET_NUM_THREADS
Возвращает фактическое число нитей в параллельной области программы.

OMP_GET_NUM_PROCS
Возвращает число процессоров, доступных приложению.

OMP_IN_PARALLEL
Возвращает .TRUE., если вызвана из параллельной области программы.

OMP_SET_DYNAMIC / OMP_GET_DYNAMIC
Устанавливает/запрашивает состояние флага, разрешающего динамически изменять число нитей.

OMP_GET_NESTED / OMP_SET_NESTED
Устанавливает/запрашивает состояние флага, разрешающего вложенный параллелизм.


Процедуры для синхронизации на базе замков

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

OMP_INIT_LOCK(var) / OMP_DESTROY_LOCK(var)
Инициализирует замок, связанный с переменной var.

OMP_SET_LOCK
Заставляет вызвавшую нить дождаться освобождения замка, а затем захватывает его.

OMP_UNSET_LOCK
Освобождает замок, если он был захвачен вызвавшей нитью.

OMP_TEST_LOCK
Пробует захватить указанный замок. Если это невозможно, возвращает .FALSE.


5. Спецификация OpenMP для языков C/C++

Спецификация OpenMP для C/C++, выпущенная на год позже фортранной, содержит в основном аналогичную функциональность.

Необходимо лишь отметить следующие моменты:

1) Вместо спецкомментариев используются директивы компилятора "#pragma omp".

2) Компилятор с поддержкой OpenMP определяет макрос "_OPENMP", который может использоваться для условной компиляции отдельных блоков, характерных для параллельной версии программы.

3) Распараллеливание применяется к for-циклам, для этого используется директива "#pragma omp for". В параллельных циклах запрещается использовать оператор break.

4) Статические (static) переменные, определенные в параллельной области программы, являются общими (shared).

5) Память, выделенная с помощью malloc(), является общей (однако указатель на нее может быть как общим, так и приватным).

6) Типы и функции OpenMP определены во включаемом файле <omp.h>.

7) Кроме обычных, возможны также "вложенные" (nested) замки - вместо логических переменных используются целые числа, и нить, уже захватившая замок, при повторном хахвате может увеличить это число.

Пример распараллеливания for-цикла в C

#pragma omp parallel for private(i) 
#pragma omp shared(x, y, n) reduction(+: a, b)
for (i=0; i<n; i++) 
{
     a = a + x[i];
     b = b + y[i];
}

6. Поддержка произвлдителями и разработчиками. Развитие стандарта.

Реализации OpenMP

1) Silicon Graphics. Поддержка OpenMP для Fortran реализована в компиляторах Fortran 77/90 семейства MIPSpro для платформы IRIX начиная с версии 7.2.1. В версии 7.3 появится поддержка OpenMP для C/C++.

2) Compaq/DEC. DIGITAL Fortran поддерживает распараллеливание в стандарте OpenMP для Фортрана.

3) Kuck & Associates (KAI). Guide - полная реализация OpenMP как для Фортрана, так и для C/C++. Подробнее: http://www.kai.com/parallel/kappro/guide/.

4) Portland Group (PGI). Пакет PGI Workstation 3.0 поддерживает OpenMP для Fortran и C/C++ на платформах Windows NT, Linux, Solaris (x86).

5) OdinMP. OpenMP-препроцессор для языка С, генерация программы в стандарте POSIX threads. См. новости.

6) Sun поддерживает стандарт OpenMP. Предполагается, что реализация OpenMP войдет в будущие версии серии продуктов Sun Performance Workshop Fortran, поставляемых с высокопроизводительными серверами Sun. Sun войдет в организацию OpenMP Architecture Board, занимающуюся стандартизацией OpenMP.

7) Компания Tera Computer анонсировала поддержку OpenMP на своих системах Tera MTA.

8) Компания Pacific-Sierra Research предлагает VAST/Parallel - распараллеливающие препроцессоры для Fortran и С с поддержкой стандарта OpenMP. Средство распараллеливания VAST/toOpenMP распознает параллелизм и автоматически добавляет в Fortran-программу директивы OpenMP.

Приложения

1) CGWAVE: Моделирование океанских волн. Совместное использование OpenMP (KAI KAP/Pro Toolset) и MPI. Подробнее: http://www.wes.hpc.mil/news/SC98/HPCchallenge4a.htm. Новости: Вручена премия за новую технологию моделирования водных поверхностей.

2) Страница на сайте KAI - параллельные приложения, в т.ч. распараллеленные с помощью OpenMP.

Развитие

OpenMP задуман как расширяемый стандарт. В конце 1999 года должна появится вторая версия спецификации для языка Fortran, в которой будут учтены требования разработчиков.

В настоящее время OpenMP ARB собирает предложения по улучшению стандарта (более подробно - на www.openmp.org).

Конференции

С 30 сентября по 1 октября 1999 г. в Lund University (Швеция) прошла первая европейская конференция, посвященная OpenMP - First European Workshop on OpenMP (EWOMP'99).


7. Справочная информация.

Документы для загрузки

  1. OpenMP: A Proposed Industry Stadard API for Shared Memory Programming. Октябрь 1997. (Postcript, 100K)
  2. OpenMP Fortran API. Октябрь 1997. (PDF, 127K)
  3. OpenMP C/C++ API. Октябрь 1998. (PDF, 201K)

Ссылки