Директивы OpenMP
Предоставляет ссылки на директивы, используемые в API OpenMP.
Visual C++ поддерживает следующие директивы OpenMP.
Для параллельного совместного использования рабочих операций:
Директива | Description |
---|---|
parallel | Определяет параллельный регион, который является кодом, который будет выполняться несколькими потоками параллельно. |
for | Приводит к тому, что работа в цикле for внутри параллельного региона будет разделена между потоками. |
сортовое железо | Определяет разделы кода, которые необходимо разделить между всеми потоками. |
single | Позволяет указать, что раздел кода должен выполняться в одном потоке, а не в основном потоке. |
Для основного потока и синхронизации:
Директива | Description |
---|---|
master | Указывает, что только основной поток должен выполнять раздел программы. |
критический | Указывает, что код выполняется только в одном потоке одновременно. |
barrier | Синхронизирует все потоки в команде; все потоки приостанавливают на барьере, пока все потоки не выполняют барьер. |
atomic | Указывает, что расположение памяти, которое будет обновляться атомарным образом. |
flush | Указывает, что все потоки имеют одинаковое представление памяти для всех общих объектов. |
упорядоченный | Указывает, что код под параллелизованным for циклом должен выполняться как последовательный цикл. |
Для среды данных:
Директива | Description |
---|---|
threadprivate | Указывает, что переменная является частной для потока. |
atomic
Указывает, что расположение памяти, которое будет обновляться атомарным образом.
#pragma omp atomic
expression
Параметры
выражение
Оператор, имеющий lvalue, расположение памяти которого требуется защитить от нескольких операций записи.
Замечания
Директива atomic
не поддерживает предложения.
Дополнительные сведения см . в атомарной конструкции 2.6.4.
Пример
// omp_atomic.cpp
// compile with: /openmp
#include <stdio.h>
#include <omp.h>
#define MAX 10
int main() {
int count = 0;
#pragma omp parallel num_threads(MAX)
{
#pragma omp atomic
count++;
}
printf_s("Number of threads: %d\n", count);
}
Number of threads: 10
barrier
Синхронизирует все потоки в команде; все потоки приостанавливают на барьере, пока все потоки не выполняют барьер.
#pragma omp barrier
Замечания
Директива barrier
не поддерживает предложения.
Дополнительные сведения см . в директиве барьера 2.6.3.
Пример
Пример использования barrier
см. в разделе master.
критический
Указывает, что код выполняется только в одном потоке одновременно.
#pragma omp critical [(name)]
{
code_block
}
Параметры
name
(Необязательно) Имя для идентификации критического кода. Имя должно быть заключено в скобки.
Замечания
Директива critical
не поддерживает предложения.
Дополнительные сведения см . в критической конструкции 2.6.2.
Пример
// omp_critical.cpp
// compile with: /openmp
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
int main()
{
int i;
int max;
int a[SIZE];
for (i = 0; i < SIZE; i++)
{
a[i] = rand();
printf_s("%d\n", a[i]);
}
max = a[0];
#pragma omp parallel for num_threads(4)
for (i = 1; i < SIZE; i++)
{
if (a[i] > max)
{
#pragma omp critical
{
// compare a[i] and max again because max
// could have been changed by another thread after
// the comparison outside the critical section
if (a[i] > max)
max = a[i];
}
}
}
printf_s("max = %d\n", max);
}
41
18467
6334
26500
19169
15724
11478
29358
26962
24464
max = 29358
flush
Указывает, что все потоки имеют одинаковое представление памяти для всех общих объектов.
#pragma omp flush [(var)]
Параметры
var
(Необязательно) Разделенный запятыми список переменных, представляющих объекты, которые необходимо синхронизировать. Если var не указан, все память сбрасывается.
Замечания
Директива flush
не поддерживает предложения.
Дополнительные сведения см . в директиве очистки 2.6.5.
Пример
// omp_flush.cpp
// compile with: /openmp
#include <stdio.h>
#include <omp.h>
void read(int *data) {
printf_s("read data\n");
*data = 1;
}
void process(int *data) {
printf_s("process data\n");
(*data)++;
}
int main() {
int data;
int flag;
flag = 0;
#pragma omp parallel sections num_threads(2)
{
#pragma omp section
{
printf_s("Thread %d: ", omp_get_thread_num( ));
read(&data);
#pragma omp flush(data)
flag = 1;
#pragma omp flush(flag)
// Do more work.
}
#pragma omp section
{
while (!flag) {
#pragma omp flush(flag)
}
#pragma omp flush(data)
printf_s("Thread %d: ", omp_get_thread_num( ));
process(&data);
printf_s("data = %d\n", data);
}
}
}
Thread 0: read data
Thread 1: process data
data = 2
для
Приводит к тому, что работа в цикле for
внутри параллельного региона будет разделена между потоками.
#pragma omp [parallel] for [clauses]
for_statement
Параметры
Предложений
(Необязательно) Ноль или более предложений см. в разделе "Примечания".
for_statement
for
Цикл. Неопределенное поведение приведет к тому, что код пользователя в цикле for
изменяет переменную индекса.
Замечания
Директива for
поддерживает следующие предложения:
Если parallel
он также указан, может быть любым предложением, clauses
принятым parallel
директивами или for
за исключением nowait
.
Дополнительные сведения см. в статье 2.4.1 для конструкции.
Пример
// omp_for.cpp
// compile with: /openmp
#include <stdio.h>
#include <math.h>
#include <omp.h>
#define NUM_THREADS 4
#define NUM_START 1
#define NUM_END 10
int main() {
int i, nRet = 0, nSum = 0, nStart = NUM_START, nEnd = NUM_END;
int nThreads = 0, nTmp = nStart + nEnd;
unsigned uTmp = (unsigned((abs(nStart - nEnd) + 1)) *
unsigned(abs(nTmp))) / 2;
int nSumCalc = uTmp;
if (nTmp < 0)
nSumCalc = -nSumCalc;
omp_set_num_threads(NUM_THREADS);
#pragma omp parallel default(none) private(i) shared(nSum, nThreads, nStart, nEnd)
{
#pragma omp master
nThreads = omp_get_num_threads();
#pragma omp for
for (i=nStart; i<=nEnd; ++i) {
#pragma omp atomic
nSum += i;
}
}
if (nThreads == NUM_THREADS) {
printf_s("%d OpenMP threads were used.\n", NUM_THREADS);
nRet = 0;
}
else {
printf_s("Expected %d OpenMP threads, but %d were used.\n",
NUM_THREADS, nThreads);
nRet = 1;
}
if (nSum != nSumCalc) {
printf_s("The sum of %d through %d should be %d, "
"but %d was reported!\n",
NUM_START, NUM_END, nSumCalc, nSum);
nRet = 1;
}
else
printf_s("The sum of %d through %d is %d\n",
NUM_START, NUM_END, nSum);
}
4 OpenMP threads were used.
The sum of 1 through 10 is 55
master
Указывает, что только основной поток должен выполнять раздел программы.
#pragma omp master
{
code_block
}
Замечания
Директива master
не поддерживает предложения.
Дополнительные сведения см . в главной конструкции 2.6.1.
Чтобы указать, что раздел кода должен выполняться в одном потоке, а не основной поток, используйте одну директиву.
Пример
// compile with: /openmp
#include <omp.h>
#include <stdio.h>
int main( )
{
int a[5], i;
#pragma omp parallel
{
// Perform some computation.
#pragma omp for
for (i = 0; i < 5; i++)
a[i] = i * i;
// Print intermediate results.
#pragma omp master
for (i = 0; i < 5; i++)
printf_s("a[%d] = %d\n", i, a[i]);
// Wait.
#pragma omp barrier
// Continue with the computation.
#pragma omp for
for (i = 0; i < 5; i++)
a[i] += i;
}
}
a[0] = 0
a[1] = 1
a[2] = 4
a[3] = 9
a[4] = 16
упорядоченного
Указывает, что код под параллелизованным for
циклом должен выполняться как последовательный цикл.
#pragma omp ordered
structured-block
Замечания
Директива ordered
должна находиться в динамической степени для или parallel for
конструкции с предложением ordered
.
Директива ordered
не поддерживает предложения.
Дополнительные сведения см. в упорядоченной конструкции 2.6.6.
Пример
// omp_ordered.cpp
// compile with: /openmp
#include <stdio.h>
#include <omp.h>
static float a[1000], b[1000], c[1000];
void test(int first, int last)
{
#pragma omp for schedule(static) ordered
for (int i = first; i <= last; ++i) {
// Do something here.
if (i % 2)
{
#pragma omp ordered
printf_s("test() iteration %d\n", i);
}
}
}
void test2(int iter)
{
#pragma omp ordered
printf_s("test2() iteration %d\n", iter);
}
int main( )
{
int i;
#pragma omp parallel
{
test(1, 8);
#pragma omp for ordered
for (i = 0 ; i < 5 ; i++)
test2(i);
}
}
test() iteration 1
test() iteration 3
test() iteration 5
test() iteration 7
test2() iteration 0
test2() iteration 1
test2() iteration 2
test2() iteration 3
test2() iteration 4
parallel
Определяет параллельный регион, который является кодом, который будет выполняться несколькими потоками параллельно.
#pragma omp parallel [clauses]
{
code_block
}
Параметры
Предложений
(Необязательно) Ноль или более предложений см. в разделе "Примечания".
Замечания
Директива parallel
поддерживает следующие предложения:
parallel
также можно использовать с директивами for и sections .
Дополнительные сведения см . в параллельной конструкции 2.3.
Пример
В следующем примере показано, как задать количество потоков и определить параллельный регион. Число потоков по умолчанию равно числу логических процессоров на компьютере. Например, если у вас есть компьютер с одним физическим процессором с поддержкой гиперпоточности, он будет иметь два логических процессора и два потока. Порядок выходных данных может отличаться на разных компьютерах.
// omp_parallel.cpp
// compile with: /openmp
#include <stdio.h>
#include <omp.h>
int main() {
#pragma omp parallel num_threads(4)
{
int i = omp_get_thread_num();
printf_s("Hello from thread %d\n", i);
}
}
Hello from thread 0
Hello from thread 1
Hello from thread 2
Hello from thread 3
разделы
Определяет разделы кода, которые необходимо разделить между всеми потоками.
#pragma omp [parallel] sections [clauses]
{
#pragma omp section
{
code_block
}
}
Параметры
Предложений
(Необязательно) Ноль или более предложений см. в разделе "Примечания".
Замечания
Директива sections
может содержать ноль или более section
директив.
Директива sections
поддерживает следующие предложения:
Если parallel
он также указан, может быть любым предложением, clauses
принятым parallel
директивами или sections
за исключением nowait
.
Дополнительные сведения см. в разделах 2.4.2.
Пример
// omp_sections.cpp
// compile with: /openmp
#include <stdio.h>
#include <omp.h>
int main() {
#pragma omp parallel sections num_threads(4)
{
printf_s("Hello from thread %d\n", omp_get_thread_num());
#pragma omp section
printf_s("Hello from thread %d\n", omp_get_thread_num());
}
}
Hello from thread 0
Hello from thread 0
отдельный
Позволяет указать, что раздел кода должен выполняться в одном потоке, а не в основном потоке.
#pragma omp single [clauses]
{
code_block
}
Параметры
Предложений
(Необязательно) Ноль или более предложений см. в разделе "Примечания".
Замечания
Директива single
поддерживает следующие предложения:
Дополнительные сведения см . в одной конструкции 2.4.3.
Чтобы указать, что раздел кода должен выполняться только в основном потоке, используйте вместо этого главную директиву.
Пример
// omp_single.cpp
// compile with: /openmp
#include <stdio.h>
#include <omp.h>
int main() {
#pragma omp parallel num_threads(2)
{
#pragma omp single
// Only a single thread can read the input.
printf_s("read input\n");
// Multiple threads in the team compute the results.
printf_s("compute results\n");
#pragma omp single
// Only a single thread can write the output.
printf_s("write output\n");
}
}
read input
compute results
compute results
write output
threadprivate
Указывает, что переменная является частной для потока.
#pragma omp threadprivate(var)
Параметры
var
Разделенный запятыми список переменных, которые необходимо сделать закрытым для потока. var должен быть глобальной или переменной пространства имен или локальной статической переменной.
Замечания
Директива threadprivate
не поддерживает предложения.
Директива threadprivate
основана на атрибуте потока с помощью ключевого слова __declspec ; ограничения для __declspec(thread)
применения threadprivate
. Например, threadprivate
переменная будет существовать в любом потоке, запущенном в процессе, а не только в тех потоках, которые являются частью команды потоков, созданной параллельной областью. Помните об этой реализации. Вы можете заметить, что конструкторы для определяемого threadprivate
пользователем типа вызываются чаще, чем ожидалось.
Вы можете использовать threadprivate
библиотеку DLL, которая статически загружена при запуске процесса, однако вы не можете использовать threadprivate
в любой библиотеке DLL, которая будет загружена через LoadLibrary , например библиотеки DLL, загруженные с помощью /DELAYLOAD (задержка импорта нагрузки), который также использует LoadLibrary
.
threadprivate
Переменная деструкторного типа не гарантирует вызова деструктора. Например:
struct MyType
{
~MyType();
};
MyType threaded_var;
#pragma omp threadprivate(threaded_var)
int main()
{
#pragma omp parallel
{}
}
Пользователи не имеют контроля за тем, когда потоки, составляющие параллельный регион, завершаются. Если эти потоки существуют, когда процесс завершается, потоки не будут получать уведомления о выходе процесса, а деструктор не будет вызываться threaded_var
в любом потоке, кроме того, который завершается (здесь основной поток). Поэтому код не должен рассчитывать на надлежащее уничтожение threadprivate
переменных.
Дополнительные сведения см . в директиве threadprivate 2.7.1.
Пример
Пример использования threadprivate
см . в разделе "Частный".