В начало!  
Сделай закладку этой страницы в Digg Сделай закладку этой страницы в Del.icoi.us Сделай закладку этой страницы в Slashdot Сделай закладку этой страницы в Technorati



Введение в OpenMP: API параллельных программ для многопроцессорных систем с общей памятью
Автор Nawal Copty   
18.08.2007 г.

Содержание

Компиляторы Sun Studio (C/C++/Фортран 95) имеют встроенную поддержку параллелизации OpenMP. OpenMP - это развивающаяся стандартная модель для параллельного программирования в среде с общей памятью. OpenMP предоставляет программистам набор прагм, процедур и переменных среды, позволяющих легко параллелизовать код. В данной статье представлено краткое введение в OpenMP и поддержку OpenMP в компиляторах Sun Studio. Особый интерес данная статья представляет для программистов, являющихся новичками в OpenMP и параллельном программировании на Фортране, C или C++.

Что такое OpenMP?

OpenMP - это набор спецификаций по параллелизации программ в среде с общей памятью. OpenMP предоставляет набор прагм, процедур и переменных среды, которые программисты могут использовать для определения параллелизма в системах с общей памятью в программах на Фортране, C и C++.

Когда прагмы OpenMP используются в программе, они дают указание компилятору, поддерживающему OpenMP, создать исполнимый модуль, который будет выполняться параллельно с использованием нескольких потоков. При этом в исходный код необходимо внести небольшие изменения (отличные от доводки для достижения максимальной производительности). Прагмы OpenMP позволяют использовать красивый, единообразный и переносимый интерфейс для параллелизации программ на различных архитектурах и системах. Спецификация OpenMP принята многими и поддерживается такими поставщиками, как Sun, Intel, IBM и SGI. (Ссылка на веб-сайт OpenMP, где имеется документ с последней версией спецификации OpenMP, находится ниже в разделе Материалы.)

Модель OpenMP позволяет параллельному программированию подняться на следующий уровень, создавая для программиста потоки и управляя ими. Все, что необходимо, это вставить соответствующие прагмы в исходный код программы и затем скомпилировать программу компилятором, поддерживающием OpenMP, с соответствующим ключом (в Sun Studio используется ключ компилятора -xopenmp). Компилятор интерпретирует прагмы и параллелизует код. При использовании компиляторов, не поддерживающих OpenMP, прагмы OpenMP игнорируются без дополнительных сообщений.

В данной статье приведены примеры использования OpenMP в программах на C и C++; аналогичные прагмы существуют и для Фортрана 95. Подробности приведены в Руководстве пользователя по OpenMP.

Прагмы OpenMP

Спецификация OpenMP определяет набор прагм. Прагма - это директива компилятора, указывающая, как обрабатывать код, следующий за прагмой. Наиболее существенной является прагма #pragma omp parallel, определяющая область параллельности.

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

Число потоков в группе, выполняющихся в области параллельности, можно контролировать несколькими способами. Один из них -- использование переменной среды OMP_NUM_THREADS. Другой способ -- вызов процедуры omp_set_num_threads(). Еще один способ -- использование выражения num_threads в сочетании с прагмой parallel.

В OpenMP поддерживаются две основных конструкции разделения работы для указания того, что работу в области параллельности следует разделить между потоками группы. Эти конструкции разделения работы - циклы и разделы. Прагма #pragma omp for используется для циклов, а прагма #pragma omp sections используется для разделов -- блоков кода, которые могут быть выполнены параллельно.

Прагма #pragma omp barrier дает всем потокам указание ожидать друг друга перед тем, как они продолжат выполнение за барьером. Как было отмечено выше, в конце области параллельности имеется неявный барьер. Прагма #pragma omp master дает компилятору указание о том, что следующий блок кода должен выполняться только главным потоком. Прагма #pragma omp single показывает, что следующий блок кода должен выполняться только одним потоком группы; этот поток не обязательно должен быть главным. Прагма #pragma omp critical может использоваться для защиты блока кода, который должен выполняться одновременно только одним потоком. Конечно, все эти прагмы имеют смысл только в контексте прагмы parallel (области параллельности).

Процедуры OpenMP

OpenMP предоставляет ряд процедур, которые можно использовать для получения сведений о потоках в программе. В их число входят omp_get_num_threads(), omp_set_num_threads(), omp_get_max_threads(), omp_in_parallel() и другие. Кроме того, OpenMP предоставляет ряд процедур блокировки, которые можно использовать для синхронизации потоков.

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

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

Важной переменной среды является переменная OMP_NUM_THREADS, указывающая число потоков в группе, которая должна использоваться для выполнения в области параллельности (включая главный поток группы). Другая широко применяемая переменная среды -- OMP_DYNAMIC. Чтобы отключить динамическое изменение числа потоков реализацией во время выполнения, для этой переменной следует установить значение FALSE. Общим правилом является не делать число потоков большим, чем число процессорных ядер в системе.

Помимо переменных среды, предусмотренных стандартом OpenMP, компиляторы Sun Studio предоставляют дополнительный набор специфических переменных среды, обеспечивающий дополнительные возможности управления средой времени выполнения. Эти переменные описаны в Руководстве пользователя по OpenMP.

Примеры использования OpenMP

На примере простой программы умножения матриц можно увидеть, как использовать OpenMP для параллелизации программы. Рассмотрим следующий небольшой фрагмент кода умножения двух матриц. Это очень простой пример, и если действительно необходимо написать хорошую подпрограмму для перемножения матриц, следует принять во внимание эффекты кэширования или использовать лучший алгоритм (Штрассена, или Копперсмита и Винограда, либо другой).

for (ii = 0; ii < dim; ii++) {
  for (jj = 0; jj < dim; jj++) {
    for (kk = 0; kk < dim; kk++) {
      array[ii][jj] += array1[ii][kk] * array2[kk][jj];
    }
  }
}

Обратите внимание, что на каждом уровне вложенности циклов тела циклов могут выполняться независимо друг от друга. Так что параллелизовать приведенный выше код очень просто: вставьте прагму #pragma omp parallel for перед самым внешним циклом (циклом по переменной ii). Лучше всего вставить прагму перед самым внешним циклом, так как это даст наибольший выигрыш в производительности. В параллелизованном цикле переменные array, array1, array2 и dim являются общими для потоков, а переменные ii, jj и kk являются частными для каждого потока. Приведенный выше код теперь становится таким:

#pragma omp parallel for shared(array, array1, array2, dim) private(ii, jj, kk)
for (ii = 0; ii < dim; ii++) {
  for (jj = 0; jj < dim; jj++) {
    for (kk = 0; kk < dim; kk++) {
      array[ii][jj] = array1[ii][kk] * array2[kk][jj];
    }
  }
}

В качестве другого примера рассмотрим следующий фрагмент кода, предназначенный для вычисления суммы значений f(x) для целых аргументов из интервала 0 <= x < n.

for (ii = 0; ii < n; ii++) {
  sum = sum + some_complex_long_fuction(a[ii]);
}

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

#pragma omp parallel for shared(sum, a, n) private(ii, value)
for (ii = 0; ii < n; ii++) {
  value = some_complex_long_fuction(a[ii]);
  #pragma omp critical
  sum = sum + value;
}

или, что лучше, можно использовать оператор приведения, в результате чего получится

#pragma omp parallel for shared(a, n) private(ii) reduction(+: sum)
for (ii = 0; ii < n; ii++) {
  sum = sum + some_complex_long_fuction(a[ii]);
}

С чего начать

Параллелизовать программу можно несколькими способами. Во-первых, следует определить, нужна ли параллелизация. Некоторые алгоритмы не годятся для параллелизации. Если создается новый проект, можно выбрать алгоритм, который может быть параллелизован. До начала попыток параллелизации очень важно убедиться, что код выполняется правильно в последовательном режиме. Определите временные характеристики при последовательном выполении, по которым можно будет принять решение о целесообразности параллелизации.

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

Когда программа готова для параллелизации, можно применить ряд функций и средств Sun Studio, которые помогут достигнуть цели. Они кратко описаны ниже.

Автоматическая параллелизация

Можно попробовать применить ключ автоматической параллелизации компилятора (-xautopar). Доверив параллелизацию компилятору, программист может параллелизовать программу без каких-либо усилий со своей стороны. Кроме того, автоматический параллелизатор может помочь определить фрагменты кода, которые можно параллелизовать с помощью прагм OpenMP, и особенности, которые могут помешать параллелизации (например, зависимости между повторениями тела цикла). Комментарии компилятора можно просмотреть, скомпилировав программу с ключом -g и использовав служебную программу er_src(1) из состава Sun Studio, например, следующим образом:

% cc -g -xautopar -c source.c

% er_src source.o

Автоопределение области действия

Одним из наиболее распространенных типов ошибок в программировании с использованием OpenMP являются ошибки в определении области действия, когда область действия переменной указывается неверно, например, переменная определяется как общая (частная), в то время как ее нужно определить как частную (общую). Функция автоопределения области действия в компиляторах Sun Studio освобождает программиста от задачи определения областей действия переменных. Поддерживаются два расширения OpenMP: оператор __auto и оператор default(__auto). Подробности приведены в Руководстве пользователя по OpenMP.

Отладчик dbx

Отладчик Sun Studio, dbx, поддерживает работу с потоками и может использоваться для отладки программы, использующей OpenMP. Для отладки программы, использующей OpenMP, следует, во-первых, скомпилировать программу без какой-либо оптимизации, использовав ключи компилятора -xopenmp=noopt -g, затем запустить dbx для отладки полученного исполнимого модуля. С помощью dbx можно устанавливать точки останова внутри области параллельности и выполнять код в области параллельности в пошаговом режиме, проверять переменные, являющиеся частными для потока, и так далее.

Анализатор производительности

Анализатор производительности Sun Studio можно использовать для определения узких мест в программе. Это средство можно использовать для определения критических областей в программе, занимающих большую часть времени. Кроме того, это средство предоставляет возможности измерения времени работы и ожидания для функций, строк исходного кода и машинных инструкций, что может помочь в определении узких мест в программе, использующей OpenMP.

Совместное использование OpenMP и MPI

Другой моделью параллельного программирования является интерфейс MPI (интерфейс передачи сообщений). В отличие от OpenMP, интерфейс MPI порождает несколько процессов, которые затем взаимодействуют по протоколу TCP/IP. Так как эти процессы не используют общее адресное пространство, они могут выполняться на удаленных компьютерах (или на кластере компьютеров). Трудно сказать, что лучше -- OpenMP или MPI. У каждого из них есть свои преимущества и недостатки. Интереснее то, что OpenMP может использоваться совместно с MPI. Обычно MPI используется для распределения работы между несколькими компьютерами, а OpenMP затем используется для параллелизации на одном компьютере.

Подводя итог, можно сказать, что компиляторы и инструменты Sun Studio имеют встроенную поддержку OpenMP и много полезных функций, помогающих параллелизовать программу. Для получения дополнительных сведений воспользуйтесь приведенным ниже разделом "Материалы".

Материалы по OpenMP

Перевод с английского: Антон Высоцкий, 2007 г.

Оригинал статьи: http://developers.sun.com/sunstudio/articles/omp-intro.html.

 

Добавить комментарий


Защитный код
Обновить