- Компиляция и запуск приложений с OpenMP
- How can I set the number of OpenMP threads from within the program?
- 1 Answer 1
- Option #1
- Option #2
- Chapter 4
- Environment Variables
- Параллельные заметки №4 — продолжаем знакомиться с конструкциями OpenMP
- Основные конструкции OpenMP
- Особенности реализации директив OpenMP
- Директивы OpenMP
- Директивы shared, private и default
- Директивы firstprivate и lastprivate
Компиляция и запуск приложений с OpenMP
OpenMP подключается опцией, соответствующей установленному компиляторy:
Так, компиляция файла exam.c на umt компилятором Intel (с учетом умолчания) выполняется командой
Число параллельных нитей OpenMP задается с помощью переменной окружения OMP_NUM_THREADS. Как правило, она устанавливается перед запуском программы из командной строки командой вида
Если число нитей больше числа выделенных процессу ядер, то, по крайней мере, одно ядро будет исполнять более одной нити, что целесообразно, например, в случае, когда часть нитей имеет меньшую вычислительную активность.
При запуске приложения пользователю необходимо указать число ядер (cpus) для каждого из процессов (task) с помощью опции —cpus-per-task= . При этом система предоставляет узел для процесса, если на нем имеется указанное число свободных ядер. Максимальное число ядер, равное 36, содержат узлы раздела apollo (см. Кластер «Уран»). Пусть задано export OMP_NUM_THREADS=12 , тогда в результате выполнения команды интерактивного запуска
одному (n=1 по умолчанию) процессу (task), т.е. задаче, пользователя будет выделено 6 ядер на узле и при вычислениях будет задействовано 12 нитей; результаты вычислений будут записаны в указанный пользователем файл exam_omp.out .
Если программе нужен запуск на узле с большим объёмом оперативной памяти, то надо добавить опцию —mem= , где — требуемый объём памяти на узле. Например, команда
запустит один процесс на узле с оперативной памятью не меньшей, чем 48 GB (о типах узлов см. Кластер «Уран»). Для заказа памяти из расчета на одно ядро используется опция —mem-per-cpu= , например,
В пакетном режиме соответствующий запуск может выглядеть так:
где файл mybat_omp содержит строки
В качестве примера программы с OpenMP можно использовать pi_omp.c.
Возможен запуск гибридных MPI/OpenMP приложений с помощью опции —ntasks-per-node= , где — число процессов на узле. Например, для запуска на двух 36-ядерных узлах 6 процессов с 12 нитями каждый можно выполнить команду:
Источник
How can I set the number of OpenMP threads from within the program?
Running the program as
limits the number of active OpenMP threads to 4, as evidenced by htop . However, if instead of binding the OMP_NUM_THREADS environment variable in Bash , I call
from main before calling any OpenMP-enabled functions, this seems to have no effect.
Why is this happening? How can I set the number of OpenMP threads from within the program, if it’s possible at all?
1 Answer 1
There are two ways 1 one can use to set the number of threads from within the program:
Option #1
Use num_threads clause in a directive that opens a parallel region:
Option #2
Use omp_set_num_threads API function before a parallel region begins:
Note: Both options take priority over OMP_NUM_THREADS environment variable, but num_threads clause has precedence over omp_set_num_threads .
This is covered in the OpenMP specification (emphasis mine):
Chapter 4
Environment Variables
[. ] Modifications to the environment variables after the program has started, even if modified by the program itself, are ignored by the OpenMP implementation. However, the settings of some of the ICVs can be modified during the execution of the OpenMP program by the use of the appropriate directive clauses or OpenMP API routines. [. ]
1) There is a third run-time option that allows to alter the number of threads executing a parallel region that follows by resetting it to 1 (master thread only) or to the number from num_threads clause or omp_set_num_threads call, which is an if clause in a directive the clause belongs to.
Источник
Параллельные заметки №4 — продолжаем знакомиться с конструкциями OpenMP
Продолжим знакомство с технологией OpenMP и рассмотрим некоторые функции и новые директивы.
В OpenMP существует ряд вспомогательных функций. Для их использования не забудьте подключить заголовочный файл .
Функции исполняющей среды
Эти функции позволяют запрашивать и задавать различные параметры среды OpenMP:
- omp_get_num_procs — возвращает число вычислительных узлов (процессоров/ядер) в компьютере.
- omp_in_parallel — позволяет потоку узнать, занимается ли он в данный момент выполнением параллельного региона.
- omp_get_num_threads — возвращает число потоков, входящих в текущую группу потоков.
- omp_set_num_thread — задает число потоков для выполнения следующего параллельного региона, который встретится текущему выполняемому потоку. Функция может помочь распределить ресурсы. Например, если мы одновременно обрабатываем звук и видео на процессоре с четырьмя ядрами, то можно создать для звука один поток, а для обработки видео — три.
- omp_get_max_threads — возвращает максимально допустимое число нитей для использования в следующей параллельной области.
- omp_set_nested — разрешает или запрещает вложенный параллелизм. Если вложенный параллелизм разрешён, то каждая нить, в которой встретится описание параллельной области, породит для её выполнения новую группу нитей и станет в ней главной.
- omp_get_nested — возвращает, разрешен ли вложенный параллелизм или нет.
Если имя функции начинается с omp_set_, то ее можно вызывать только вне параллельных регионов. Все остальные функции можно использовать как внутри параллельных регионов, так и вне таковых.
Функции синхронизации/блокировки
OpenMP позволяет строить параллельный код без использования этих функций, так как имеются директивы, позволяющие осуществлять определенные виды синхронизации. Однако в ряде случаев эти функции удобны и даже необходимы.
В OpenMP два типа блокировок: простые и вложенные. Вложенные имеют суффикс «nest». Блокировки могут находиться в одном из трех состояний — неинициализированном, заблокированном и разблокированном.
- omp_init_lock/omp_init_nest_lock — инициализация переменной типа omp_lock_t/omp_nest_lock_t. Аналог InitializeCriticalSection.
- omp_destroy_lock/omp_destroy_nest_lock — освобождение переменной типа omp_lock_t/omp_nest_lock_t. Аналог DeleteCriticalSection.
- omp_set_lock/omp_set_nest_lock — один поток выставляет блокировку, а остальные потоки ждут, пока поток, вызвавшая эту функцию, не снимет блокировку с помощью функции omp_unset_lock(). Аналог EnterCriticalSection.
- omp_unset_lock/omp_unset_nest_lock — снятие блокировки. Аналог LeaveCriticalSection.
- omp_test_lock/omp_test_nest_lock — неблокирующая попытка захвата замка. Данная функция пробует захватить указанный замок. Если это удалось, то для простого замка функция возвращает 1. Если замок захватить не удалось, то возвращается 0. Аналог TryEnterCriticalSection.
Простые блокировки (omp_lock_t) не могут быть установлены более одного раза, даже тем же потоком. Вкладываемые блокировки (omp_nest_lock_t) идентичны простым с тем исключением, что когда поток пытается установить уже принадлежащую ему вкладываемую блокировку, он не блокируется.
Приведем пример кода, использующий описанные функции. Все создаваемые потоки по очереди выведут сообщения «Begin work» и «End work». Между этими двумя сообщениями от одного потока могут встретиться сообщения от других потоков, выводимых при неудачной попытке войти в закрытую секцию.
На машине с четырьмя ядрами может быть получен следующий вывод:
Begin work, thread 0
Wait. thread 1
Wait. thread 2
Wait. thread 3
Wait. thread 2
Wait. thread 3
Wait. thread 1
End work, thread 0
Begin work, thread 2
Wait. thread 3
Wait. thread 1
Wait. thread 3
Wait. thread 1
End work, thread 2
Begin work, thread 3
Wait. thread 1
Wait. thread 1
End work, thread 3
Begin work, thread 1
End work, thread 1
Функции работы с таймерами
- omp_get_wtime — возвращает в вызвавшем потоке астрономическое время в секундах (вещественное число двойной точности — double), прошедшее с некоторого момента в прошлом. Если некоторый участок программы окружить вызовами данной функции, то разность возвращаемых значений покажет время работы данного участка.
- omp_get_wtick() — возвращает в вызвавшем потоке разрешающую способность таймера в секундах, то есть точность таймера.
На этом знакомство с функциями мы завершим и рассмотрим парочку новых директив. Это директивы можно назвать опциями создаваемых параллельных регионов.
if (условие)
Выполнение параллельной области по условию. Создание нескольких потоков осуществляется только при выполнении некоторого условия. Если условие не выполнено, то код выполняется в последовательном режиме.
num_threads
Явное задание количества потоков, которые будут выполнять параллельную область. По умолчанию выбирается последнее значение, установленное с помощью функции omp_set_num_threads().
Если мы модифицируем пример приведенный выше следующим образом:
то получим следующий вывод:
В следующем выпуске «Параллельных заметок» мы продолжим…
Источник
Основные конструкции OpenMP
Особенности реализации директив OpenMP
В этом разделе рассмотрим некоторые специфические особенности реализации директив OpenMP.
Количество потоков в параллельной программе определяется либо значением переменной окружения OMP_NUM_THREADS , либо специальными функциями, вызываемыми внутри самой программы. Задание переменной окружения OMP_NUM_THREADS в операционной системе Linux осуществляется следующим образом с помощью команды
Здесь было задано 256 параллельных потоков.
Просмотреть состояние переменной окружения OMP_NUM_THREADS можно, например, с помощью следующей команды:
Каждый поток имеет свой номер thread_number , начинающийся от нуля (для главного потока) и заканчивающийся OMP_NUM_THREADS-1 . Определить номер текущего потока можно с помощью функции omp_get_thread_num() .
Каждый поток выполняет структурный блок программы, включенный в параллельный регион. В общем случае синхронизации между потоками нет, и заранее предсказать завершение потока нельзя. Управление синхронизацией потоков и данных осуществляется программистом внутри программы. После завершения выполнения параллельного структурного блока программы все параллельные потоки за исключением главного прекращают свое существование.
Пример ветвления во фрагменте программы, написанной на языке C/C++, в зависимости от номера параллельного потока показан в примере 2.8.
В этом примере в первой строке параллельного блока вызывается функция omp_get_thread_num , возвращающая номер параллельного потока. Этот номер сохраняется в локальной переменной myid .Далее в зависимости от значения переменной myid вызывается либо функция do_something() в первом параллельном потоке с номером 0, либо функция do_something_else(myid) во всех остальных параллельных потоках.
В OpenMP существуют различные режимы выполнения ( Execution Mode ) параллельных структурных блоков. Возможны следующие режимы:
- Динамический режим (Dynamic Mode). Этот режим установлен по умолчанию и определяется заданием переменной окружения OMP_DYNAMIC в операционной системе Linux. Задать эту переменную можно, например, с помощью следующей команды операционной системы Linux:
Кроме того, существует возможность задать этот режим и саму переменную внутри программы, вызвав функцию
Отметим, что на компьютерах Silicon Graphics S350 и Kraftway G-Scale S350 вторая возможность отсутствует. В динамическом режиме количество потоков определяется самой операционной системой в соответствии со значением переменной окружения OMP_NUM_THREADS .В процессе выполнения параллельной программы при переходе от одной области распараллеливания к другой эта переменная может изменять свое значение;
Кроме того, существует возможность задать этот режим и саму переменную внутри программы, вызвав функцию
Однако на компьютерах Silicon Graphics S350 и Kraftway G-Scale S350 вторая возможность отсутствует;
Кроме того, существует возможность задать этот режим и саму переменную внутри программы, вызвав функцию
Однако на компьютерах Silicon Graphics S350 и Kraftway G-Scale S350 вторая возможность отсутствует.
Директивы OpenMP
Теперь перейдем к подробному рассмотрению директив OpenMP и механизмов их реализации.
Директивы shared, private и default
Эти директивы (предложения OpenMP) используются для описания типов переменных внутри параллельных потоков.
определяет переменные var1, var2, …, varN как общие переменные для всех потоков. Они размещаются в одной и той же области памяти для всех потоков.
определяет переменные var1, var2, …, varN как локальные переменные для каждого из параллельных потоков. В каждом из потоков эти переменные имеют собственные значения и относятся к различным областям памяти: локальным областям памяти каждого конкретного параллельного потока.
В качестве иллюстрации использования директив OpenMP shared и private рассмотрим фрагмент программы (Пример 2.9). В этом примере переменная a определена как общая и является идентификатором одномерного массива. Переменные myid и x определены как локальные переменные для каждого из параллельных потоков. В каждом из параллельных потоков локальные переменные получают собственные значения. После чего при выполнении условия x значение локальной переменной x присваивается myid -й компоненте общего для всех потоков массива a . Значение x будет неопределенным, если не определить x как переменную типа private . Отметим, что значения private -переменных не определены до и после блока параллельных вычислений.
задает тип всех переменных, определяемых по умолчанию в последующем параллельном структурном блоке как shared , private или none . Например, если во фрагменте программы (примере 2.9) вместо
то определяемые далее по умолчанию переменные myid и x будут автоматически определены как private .
Директивы firstprivate и lastprivate
Директивы (предложения) OpenMP firstprivate и lastprivate используются для описания локальных переменных, инициализируемых внутри параллельных потоков. Переменные, описанные как firstprivate , получают свои значения из последовательной части программы. Переменные, описанные как lastprivate , сохраняют свои значения при выходе из параллельных потоков при условии их последовательного выполнения.
определяет переменные var1, var2, . varN как локальные переменные для каждого из параллельных потоков, причем инициализация значений этих переменных происходит в самом начале параллельного структурного блока по значениям из предшествующего последовательного структурного блока программы.
В качестве иллюстрации рассмотрим фрагмент программы (Пример 2.10). В этом примере в каждом параллельном потоке используется своя переменная c , но значение этой переменной перед входом в параллельный блок программы берется из предшествующего последовательного блока.
Источник