2. 지시문

지시문은 C 및 C++ 표준에 정의된 #pragma 지시문을 기반으로 합니다. OpenMP C 및 C++ API를 지원하는 컴파일러에는 모든 OpenMP 컴파일러 지시문을 활성화하고 해석할 수 있는 명령줄 옵션이 포함됩니다.

2.1 지시문 형식

OpenMP 지시문의 구문은 공식적으로는 부록 C의 구문에 따라 지정되며 비공식적으로는 다음과 같습니다.

#pragma omp directive-name  [clause[ [,] clause]...] new-line

각 지시문은 동일한 이름을 가진 다른(OpenMP가 아닌 또는 OpenMP에 대한 공급업체 확장) pragma 지시문과의 충돌 가능성을 줄이기 위해 #pragma omp로 시작합니다. 지시문의 나머지 부분은 컴파일러 지시문에 대한 C 및 C++ 표준 규칙을 따릅니다. 특히, # 앞뒤에 공백을 사용할 수 있으며, 지시문에서 단어를 구분하기 위해 공백을 사용해야 하는 경우도 있습니다. #pragma omp 다음에 오는 전처리 토큰은 매크로 대체 대상입니다.

지시문은 대/소문자를 구분합니다. 지시문에 절이 나타나는 순서는 중요하지 않습니다. 지시문에 대한 조항은 각 조항의 설명에 나열된 제한 사항에 따라 필요에 따라 반복될 수 있습니다. variable-list가 절에 나타나면 변수만 지정해야 합니다. 지시문당 하나의 directive-name만 지정할 수 있습니다. 예를 들어, 다음 지시문은 허용되지 않습니다.

/* ERROR - multiple directive names not allowed */
#pragma omp parallel barrier

OpenMP 지시문은 구조화된 블록이어야 하는 최대 하나의 후속 문에 적용됩니다.

2.2 조건부 컴파일

_OPENMP 매크로 이름은 OpenMP 호환 구현에 의해 승인된 사양의 연도와 월이 되는 십진 상수 yyyymm으로 정의됩니다. 이 매크로는 #define 또는 #undef 전처리 지시문의 주제가 아니어야 합니다.

#ifdef _OPENMP
iam = omp_get_thread_num() + index;
#endif

공급업체가 OpenMP에 대한 확장을 정의하는 경우 미리 정의된 매크로를 추가로 지정할 수 있습니다.

2.3 parallel 구문

다음 지시문은 많은 스레드가 병렬로 실행되는 프로그램 영역인 병렬 영역을 정의합니다. 이 지시문은 병렬 실행을 시작하는 기본 구문입니다.

#pragma omp parallel [clause[ [, ]clause] ...] new-line   structured-block

은 다음 중 하나입니다.

  • if( scalar-expression )
  • private( variable-list )
  • firstprivate( variable-list )
  • default(shared | none)
  • shared( variable-list )
  • copyin( variable-list )
  • reduction( operator : variable-list )
  • num_threads( integer-expression )

스레드가 병렬 구문에 도달하면 다음 경우 중 하나가 참일 경우 스레드 팀이 만들어집니다.

  • if 절이 없습니다.
  • if 식은 0이 아닌 값으로 평가됩니다.

이 스레드는 스레드 번호가 0인 팀의 마스터 스레드가 되며, 마스터 스레드를 포함한 팀의 모든 스레드가 해당 영역을 병렬로 실행합니다. if 식의 값이 0이면 영역이 직렬화됩니다.

요청된 스레드 수를 결정하기 위해 다음 규칙이 순서대로 고려됩니다. 조건이 충족되는 첫 번째 규칙이 적용됩니다.

  1. num_threads 절이 있는 경우 정수 식의 값은 요청된 스레드 수입니다.

  2. omp_set_num_threads 라이브러리 함수가 호출된 경우 가장 최근에 실행된 호출의 인수 값은 요청된 스레드 수입니다.

  3. 환경 변수 OMP_NUM_THREADS가 정의된 경우 이 환경 변수의 값은 요청된 스레드 수입니다.

  4. 위의 방법 중 어느 것도 사용되지 않으면 요청된 스레드 수는 구현에 따라 정의됩니다.

num_threads 절이 있으면 이 절이 적용되는 병렬 영역에 대해서만 omp_set_num_threads 라이브러리 함수 또는 OMP_NUM_THREADS 환경 변수에서 요청한 스레드 수를 우선합니다. 이후의 병렬 영역은 영향을 받지 않습니다.

병렬 영역을 실행하는 스레드 수는 스레드 수의 동적 조정이 사용하도록 설정되었는지 여부에 따라 달라집니다. 동적 조정이 사용하지 않도록 설정된 경우 요청된 수의 스레드가 병렬 영역을 실행합니다. 동적 조정이 사용하도록 설정된 경우 요청된 스레드 수는 병렬 영역을 실행할 수 있는 최대 스레드 수입니다.

스레드 수의 동적 조정이 사용하지 않도록 설정되어 있는 동안 병렬 영역이 발생하고 병렬 영역에 대해 요청된 스레드 수가 런타임 시스템에서 제공할 수 있는 수보다 많은 경우 프로그램의 동작은 구현에 따라 정의됩니다. 예를 들어, 구현은 프로그램 실행을 중단하거나 병렬 영역을 직렬화할 수 있습니다.

omp_set_dynamic 라이브러리 함수와 OMP_DYNAMIC 환경 변수를 사용하여 스레드 수의 동적 조정을 사용 및 사용하지 않도록 설정할 수 있습니다.

지정된 시간에 실제로 스레드를 호스팅하는 실제 프로세서의 수는 구현에 따라 정의됩니다. 일단 만들어지면 팀의 스레드 수는 해당 병렬 영역이 지속되는 동안 일정하게 유지됩니다. 이는 사용자가 명시적으로 변경하거나 런타임 시스템에 의해 자동으로 한 병렬 영역에서 다른 영역으로 변경될 수 있습니다.

병렬 영역의 동적 범위 내에 포함된 문은 각 스레드에 의해 실행되며, 각 스레드는 다른 스레드와 다른 문 경로를 실행할 수 있습니다. 병렬 영역의 어휘 범위 외부에서 발견되는 지시문을 분리된 지시문이라고 합니다.

병렬 영역 끝에는 암시적인 장벽이 있습니다. 팀의 마스터 스레드만 병렬 영역 끝에서 실행을 계속합니다.

병렬 영역을 실행하는 팀의 스레드가 다른 병렬 구문을 만나면 새 팀을 만들고 해당 스레드가 새 팀의 마스터가 됩니다. 중첩된 병렬 영역은 기본적으로 직렬화됩니다. 결과적으로 기본적으로 중첩된 병렬 영역은 하나의 스레드로 구성된 팀에 의해 실행됩니다. 기본 동작은 런타임 라이브러리 함수 omp_set_nested 또는 환경 변수 OMP_NESTED를 사용하여 변경할 수 있습니다. 그러나 중첩된 병렬 영역을 실행하는 팀의 스레드 수는 구현에 따라 정의됩니다.

parallel 지시문에 대한 제한 사항은 다음과 같습니다.

  • 지시문에는 최대 하나의 if 절이 나타날 수 있습니다.

  • if 식 또는 num_threads 식 내부에 부작용이 발생하는지 여부는 지정되지 않았습니다.

  • 병렬 영역 내에서 실행되는 throw는 동일한 구조화된 블록의 동적 범위 내에서 실행을 다시 시작해야 하며 예외를 throw한 동일한 스레드에 의해 catch되어야 합니다.

  • 지시문에는 단일 num_threads 절만 나타날 수 있습니다. num_threads 식은 병렬 영역의 컨텍스트 외부에서 평가되며 양의 정수 값으로 평가되어야 합니다.

  • ifnum_threads 절의 평가 순서는 지정되지 않습니다.

상호 참조

2.4 작업 공유 구문

작업 공유 구문은 연관된 문의 실행을 발생한 팀 멤버 간에 배포합니다. 작업 공유 지시문은 새 스레드를 시작하지 않으며 작업 공유 구문에 대한 진입 장벽이 없습니다.

작업 공유 구문과 barrier 지시문의 시퀀스는 팀의 모든 스레드에 대해 동일해야 합니다.

OpenMP는 다음과 같은 작업 공유 구문을 정의하며 이러한 구문은 다음 섹션에서 설명됩니다.

2.4.1 for 구문

for 지시문은 관련 루프의 반복이 병렬로 실행되도록 지정하는 반복 작업 공유 구문을 식별합니다. for 루프의 반복은 바인딩되는 병렬 구문을 실행하는 팀에 이미 존재하는 스레드 전체에 분산됩니다. for 구문의 구문은 다음과 같습니다.

#pragma omp for [clause[[,] clause] ... ] new-line for-loop

절은 다음 중 하나입니다.

  • private( variable-list )
  • firstprivate( variable-list )
  • lastprivate( variable-list )
  • reduction( operator : variable-list )
  • ordered
  • schedule( kind [, chunk_size] )
  • nowait

for 지시문은 해당 for 루프의 구조에 제한을 둡니다. 특히 해당 for 루프는 정식 형태를 가져야 합니다.

for ( init-expr ; var logical-op b ; incr-expr )

init-expr
(다음 버전 중 하나)

  • var = lb
  • integer-type var = lb

incr-expr
(다음 버전 중 하나)

  • ++ var
  • var ++
  • -- var
  • var --
  • var += incr
  • var -= incr
  • var = var + incr
  • var = incr + var
  • var = var - incr

var
부호 있는 정수 변수입니다. 이 변수가 공유되는 경우 for 기간 동안 암시적으로 프라이빗으로 설정됩니다. for 문의 본문 내에서 이 변수를 수정하지 마세요. 변수가 lastprivate으로 지정되지 않은 경우 루프 이후의 해당 값은 불확실합니다.

logical-op
(다음 버전 중 하나)

  • <
  • <=
  • >
  • >=

lb, bincr
루프 고정 정수 식입니다. 이러한 식을 평가하는 동안 동기화가 이루어지지 않으므로 평가된 부작용으로 인해 불확실한 결과가 생성됩니다.

정규형을 사용하면 루프 시작 시 루프 반복 횟수를 계산할 수 있습니다. 이 계산은 정수 승격 후 var 형식의 값으로 수행됩니다. 특히, b - lb + incr 값을 해당 형식으로 표현할 수 없는 경우 결과는 불확실합니다. 또한, logical-op< 또는 <=인 경우 incr-expr은 루프가 반복될 때마다 var이 증가하도록 해야 합니다. logical-op> 또는 >=인 경우 incr-expr은 루프가 반복될 때마다 var이 작아지도록 해야 합니다.

schedule 절은 for 루프의 반복이 팀의 스레드 간에 분할되는 방식을 지정합니다. 프로그램의 정확성은 어떤 스레드가 특정 반복을 실행하는지에 따라 달라져서는 안 됩니다. 지정된 경우 chunk_size 값은 양수 값을 갖는 루프 고정 정수 식이어야 합니다. 이 식을 평가하는 동안 동기화가 이루어지지 않으므로 평가된 모든 부작용은 불확실한 결과를 생성합니다. schedule kind는 다음 값 중 하나일 수 있습니다.

표 2-1: schedulekind

설명
static schedule(static, chunk_size )가 지정되면 반복은 chunk_size에 지정된 크기의 청크로 나뉩니다. 청크는 스레드 번호 순서에 따라 라운드 로빈 방식으로 팀의 스레드에 정적으로 할당됩니다. chunk_size가 지정되지 않으면 반복 공간은 크기가 대략 동일한 청크로 나누어지고 각 스레드에 하나의 청크가 할당됩니다.
dynamic schedule(dynamic, chunk_size )가 지정되면 반복은 각각 chunk_size 반복을 포함하는 일련의 청크로 나뉩니다. 각 청크는 할당을 기다리는 스레드에 할당됩니다. 스레드는 반복 청크를 실행한 후 할당할 청크가 더 이상 남지 않을 때까지 다음 할당을 기다립니다. 할당할 마지막 청크의 반복 횟수는 더 적을 수 있습니다. chunk_size가 지정되지 않으면 기본값은 1입니다.
단계별 schedule(guided, chunk_size )가 지정되면 반복은 크기가 감소하는 청크의 스레드에 할당됩니다. 스레드가 할당된 반복 청크를 마치면 아무것도 남지 않을 때까지 다른 청크가 동적으로 할당됩니다. chunk_size가 1인 경우 각 청크의 크기는 대략 할당되지 않은 반복 수를 스레드 수로 나눈 값입니다. 이 크기는 거의 기하급수적으로 1로 감소합니다. 1보다 큰 k 값을 갖는 chunk_size의 경우 크기는 거의 기하급수적으로 k로 감소합니다. 단, 마지막 청크의 크기는 k 반복보다 작을 수 있습니다. chunk_size가 지정되지 않으면 기본값은 1입니다.
runtime schedule(runtime)을 지정하면 스케줄링에 관한 결정이 런타임까지 연기됩니다. 청크의 schedule kind와 크기는 환경 변수 OMP_SCHEDULE을 설정하여 런타임 시 선택할 수 있습니다. 이 환경 변수가 설정되지 않은 경우 결과 schedule은 구현에 따라 정의됩니다. schedule(runtime)이 지정된 경우 chunk_size를 지정하면 안 됩니다.

명시적으로 정의된 schedule 절이 없으면 기본 schedule은 구현에 따라 정의됩니다.

OpenMP 호환 프로그램은 올바른 실행을 위해 특정 schedule에 의존해서는 안 됩니다. 프로그램은 위에 제공된 설명과 정확하게 일치하는 schedule kind에 의존해서는 안 됩니다. 왜냐하면 서로 다른 컴파일러에 걸쳐 동일한 schedule kind의 구현에 변형이 있을 수 있기 때문입니다. 설명을 사용하여 특정 상황에 적절한 schedule을 선택할 수 있습니다.

ordered 지시문이 for 구문에 바인딩될 때 ordered 절이 있어야 합니다.

nowait 절이 지정되지 않는 한 for 구문 끝에 암시적 장벽이 있습니다.

for 지시문에 대한 제한 사항은 다음과 같습니다.

  • for 루프는 구조화된 블록이어야 하며, 또한 실행이 break 문에 의해 종료되어서는 안 됩니다.

  • for 지시문과 연관된 for 루프의 루프 제어 식 값은 팀의 모든 스레드에 대해 동일해야 합니다.

  • for 루프 반복 변수는 부호 있는 정수 형식이어야 합니다.

  • for 지시문에는 단일 schedule 절만 나타날 수 있습니다.

  • for 지시문에는 단일 ordered 절만 나타날 수 있습니다.

  • for 지시문에는 단일 nowait 절만 나타날 수 있습니다.

  • chunk_size, lb, b 또는 incr 식 내에서 부작용이 발생하는지 여부와 빈도는 지정되지 않습니다.

  • chunk_size 식의 값은 팀의 모든 스레드에 대해 동일해야 합니다.

상호 참조

2.4.2 sections 구문

sections 지시문은 팀의 스레드 간에 나누어질 구문 집합을 지정하는 비반복적 작업 공유 구문을 식별합니다. 각 섹션은 팀의 스레드에 의해 한 번 실행됩니다. sections 지시문의 구문은 다음과 같습니다.

#pragma omp sections [clause[[,] clause] ...] new-line
   {
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-linestructured-block ]
...
}

절은 다음 중 하나입니다.

  • private( variable-list )
  • firstprivate( variable-list )
  • lastprivate( variable-list )
  • reduction( operator : variable-list )
  • nowait

각 섹션 앞에는 section 지시문이 있지만, 첫 번째 섹션에서는 section 지시문이 선택 사항입니다. section 지시문은 sections 지시문의 어휘 범위 내에 나타나야 합니다. nowait가 지정되지 않는 한 sections 구문 끝에 암시적 장벽이 있습니다.

sections 지시문에 대한 제한 사항은 다음과 같습니다.

  • section 지시문은 sections 지시문의 어휘 범위 외부에 나타나서는 안 됩니다.

  • sections 지시문에는 단일 nowait 절만 나타날 수 있습니다.

상호 참조

  • private, firstprivate, lastprivatereduction 절(섹션 2.7.2)

2.4.3 single 구문

single 지시문은 연관된 구조화된 블록이 팀의 하나의 스레드(반드시 마스터 스레드일 필요는 없음)에 의해서만 실행되도록 지정하는 구문을 식별합니다. single 지시문의 구문은 다음과 같습니다.

#pragma omp single [clause[[,] clause] ...] new-linestructured-block

절은 다음 중 하나입니다.

  • private( variable-list )
  • firstprivate( variable-list )
  • copyprivate( variable-list )
  • nowait

nowait 절이 지정되지 않는 한 single 구문 뒤에 암시적 장벽이 있습니다.

single 지시문에 대한 제한 사항은 다음과 같습니다.

  • single 지시문에는 단일 nowait 절만 나타날 수 있습니다.
  • copyprivate 절은 nowait 절과 함께 사용하면 안 됩니다.

상호 참조

2.5 결합된 병렬 작업 공유 구문

결합된 병렬 작업 공유 구문은 작업 공유 구문이 하나만 있는 병렬 영역을 지정하는 바로 가기입니다. 이러한 지시문의 의미 체계는 parallel 지시문과 단일 작업 공유 구문을 명시적으로 지정하는 것과 동일합니다.

다음 섹션에서는 결합된 병렬 작업 공유 구문을 설명합니다.

2.5.1 parallel for 구문

parallel for 지시문은 단일 for 지시문만 포함하는 parallel 영역에 대한 바로 가기입니다. parallel for 지시문의 구문은 다음과 같습니다.

#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop

이 지시문은 nowait 절을 제외하고 parallel 지시문과 for 지시문의 모든 절을 동일한 의미와 제한으로 허용합니다. 의미 체계는 parallel 지시문 바로 뒤에 for 지시문이 오는 것을 명시적으로 지정하는 것과 동일합니다.

상호 참조

2.5.2 parallel sections 구문

parallel sections 지시문은 단일 sections 지시문만 있는 parallel 지역을 지정하기 위한 단축 형식을 제공합니다. 의미 체계는 parallel 지시문 바로 뒤에 sections 지시문이 오는 것을 명시적으로 지정하는 것과 동일합니다. parallel sections 지시문의 구문은 다음과 같습니다.

#pragma omp parallel sections  [clause[[,] clause] ...] new-line
   {
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-linestructured-block  ]
   ...
}

nowait 절을 제외하고 parallelsections 지시문에서 수락되는 절 중 하나일 수 있습니다.

상호 참조

2.6 Master 및 synchronization 지시문

다음 섹션에서는 다음을 설명합니다.

2.6.1 master 구문

master 지시문은 팀의 마스터 스레드에 의해 실행되는 구조화된 블록을 지정하는 구문을 식별합니다. master 지시문의 구문은 다음과 같습니다.

#pragma omp master new-linestructured-block

팀의 다른 스레드는 연관된 구조화된 블록을 실행하지 않습니다. 마스터 구문에 들어가거나 나올 때 암시적인 장벽이 없습니다.

2.6.2 critical 구문

critical 지시문은 연관된 구조화된 블록의 실행을 한 번에 단일 스레드로 제한하는 구문을 식별합니다. critical 지시문의 구문은 다음과 같습니다.

#pragma omp critical [(name)]  new-linestructured-block

선택 사항인 name을 사용하여 중요 지역을 식별할 수 있습니다. 중요 영역을 식별하는 데 사용되는 식별자는 외부 연결을 가지며 레이블, 태그, 멤버 및 일반 식별자에서 사용되는 네임스페이스와는 별도의 네임스페이스에 있습니다.

스레드는 동일한 이름을 가진 임계 영역(프로그램의 어느 위치에서든)을 실행하는 다른 스레드가 없을 때까지 임계 영역의 시작 부분에서 기다립니다. 이름이 지정되지 않은 모든 critical 지시문은 지정되지 않은 동일한 이름에 매핑됩니다.

2.6.3 barrier 지시문

barrier 지시문은 팀의 모든 스레드를 동기화합니다. 발생하면 팀의 각 스레드는 다른 모든 스레드가 이 지점에 도달할 때까지 기다립니다. barrier 지시문의 구문은 다음과 같습니다.

#pragma omp barrier new-line

팀의 모든 스레드가 장벽을 만난 후 팀의 각 스레드는 장벽 지시문 뒤의 문을 병렬로 실행하기 시작합니다. barrier 지시문에는 구문의 일부로 C 언어 문이 없기 때문에 프로그램 내 배치에 몇 가지 제한 사항이 있습니다. 형식 문법에 대한 자세한 내용은 부록 C를 참조하세요. 아래 예에서는 이러한 제한 사항을 보여 줍니다.

/* ERROR - The barrier directive cannot be the immediate
*          substatement of an if statement
*/
if (x!=0)
   #pragma omp barrier
...

/* OK - The barrier directive is enclosed in a
*      compound statement.
*/
if (x!=0) {
   #pragma omp barrier
}

2.6.4 atomic 구문

atomic 지시문은 여러 개의 동시 쓰기 스레드 가능성에 노출시키지 않고 특정 메모리 위치가 원자성으로 업데이트되도록 보장합니다. atomic 지시문의 구문은 다음과 같습니다.

#pragma omp atomic new-lineexpression-stmt

식 문의 형식은 다음 중 하나여야 합니다.

  • x binop = expr
  • x ++
  • ++ x
  • x --
  • -- x

앞의 식에서:

  • x는 스칼라 형식을 사용하는 lvalue 식입니다.

  • expr은 스칼라 형식의 식이며 x로 지정된 개체를 참조하지 않습니다.

  • binop는 오버로드된 연산자가 아니며 +, *, -, /, &, ^, |, << 또는 >> 중 하나입니다.

구현이 모든 atomic 지시문을 동일한 고유 name을 가진 critical 지시문으로 바꾸는지 여부는 구현에 따라 정의되지만 atomic 지시문은 더 나은 최적화를 허용합니다. 최소한의 오버헤드로 원자성 업데이트를 수행할 수 있는 하드웨어 명령을 사용할 수 있는 경우가 많습니다.

x로 지정된 개체의 로드 및 저장만 원자성입니다. expr의 평가는 원자성이 아닙니다. 경합 상태를 방지하려면 경합 상태가 없는 것으로 알려진 업데이트를 제외하고 병렬 위치의 모든 업데이트를 atomic 지시문으로 보호해야 합니다.

atomic 지시문에 대한 제한 사항은 다음과 같습니다.

  • 프로그램 전체에서 스토리지 위치 x에 대한 모든 원자성 참조는 호환 가능한 형식을 가져야 합니다.

예제

extern float a[], *p = a, b;
/* Protect against races among multiple updates. */
#pragma omp atomic
a[index[i]] += b;
/* Protect against races with updates through a. */
#pragma omp atomic
p[i] -= 1.0f;

extern union {int n; float x;} u;
/* ERROR - References through incompatible types. */
#pragma omp atomic
u.n++;
#pragma omp atomic
u.x -= 1.0f;

2.6.5 flush 지시문

명시적이든 암시적이든 flush 지시문은 팀의 모든 스레드가 메모리에 있는 특정 개체(아래 지정)에 대한 일관된 보기를 갖도록 구현이 필요한 "교차 스레드" 시퀀스 지점을 지정합니다. 이는 해당 개체를 참조하는 식의 이전 평가가 완료되었으며 후속 평가가 아직 시작되지 않았음을 의미합니다. 예를 들어, 컴파일러는 레지스터에서 메모리로 개체 값을 복원해야 하며, 하드웨어는 쓰기 버퍼를 메모리로 플러시하고 메모리에서 개체 값을 다시 로드해야 할 수 있습니다.

flush 지시문의 구문은 다음과 같습니다.

#pragma omp flush [(variable-list)]  new-line

동기화가 필요한 개체를 모두 변수로 지정할 수 있는 경우 해당 변수는 선택적 variable-list에 지정할 수 있습니다. 포인터가 variable-list에 있으면 포인터가 참조하는 개체가 아닌 포인터 자체가 플러시됩니다.

variable-list가 없는 flush 지시문은 액세스할 수 없는 개체를 제외한 모든 공유 개체를 자동 저장 기간과 동기화합니다. (이는 variable-list가 있는 flush보다 오버헤드가 더 많을 가능성이 높습니다.) 다음 지시문에는 variable-list가 없는 flush 지시문이 암시됩니다.

  • barrier
  • critical 진입 및 종료에서
  • ordered 진입 및 종료에서
  • parallel 진입 및 종료에서
  • for 종료에서
  • sections 종료에서
  • single 종료에서
  • parallel for 진입 및 종료에서
  • parallel sections 진입 및 종료에서

nowait 절이 있는 경우 지시문이 암시되지 않습니다. 다음 중 어떤 것에도 flush 지시문이 암시되지 않는다는 점에 유의해야 합니다.

  • for 진입에서
  • master 진입 및 종료에서
  • sections 진입에서
  • single 진입에서

휘발성 한정 형식으로 개체의 값에 액세스하는 참조는 이전 시퀀스 포인트에 해당 개체를 지정하는 flush 지시문이 있는 것처럼 동작합니다. 휘발성 한정 형식으로 개체의 값을 수정하는 참조는 후속 시퀀스 포인트에 해당 개체를 지정하는 flush 지시문이 있는 것처럼 동작합니다.

flush 지시문에는 구문의 일부로 C 언어 문이 없기 때문에 프로그램 내 배치에 몇 가지 제한 사항이 있습니다. 형식 문법에 대한 자세한 내용은 부록 C를 참조하세요. 아래 예에서는 이러한 제한 사항을 보여 줍니다.

/* ERROR - The flush directive cannot be the immediate
*          substatement of an if statement.
*/
if (x!=0)
   #pragma omp flush (x)
...

/* OK - The flush directive is enclosed in a
*      compound statement
*/
if (x!=0) {
   #pragma omp flush (x)
}

flush 지시문에 대한 제한 사항은 다음과 같습니다.

  • flush 지시문에 지정된 변수에는 참조 형식이 있어서는 안 됩니다.

2.6.6 ordered 구문

ordered 지시문 다음에 오는 구조화된 블록은 순차 루프에서 반복이 실행되는 순서대로 실행됩니다. ordered 지시문의 구문은 다음과 같습니다.

#pragma omp ordered new-linestructured-block

ordered 지시문은 for 또는 parallel for 구문의 동적 범위 내에 있어야 합니다. ordered 구문이 바인딩되는 for 또는 parallel for 지시문에는 섹션 2.4.1에 설명된 대로 지정된 ordered 절이 있어야 합니다. ordered 절을 사용하여 for 또는 parallel for 구문을 실행할 때 ordered 구문은 루프의 순차적 실행에서 실행되는 순서대로 엄격하게 실행됩니다.

ordered 지시문에 대한 제한 사항은 다음과 같습니다.

  • for 구문이 포함된 루프 반복은 동일한 순서의 지시문을 두 번 이상 실행해서는 안 되며, ordered 지시문을 두 번 이상 실행해서는 안 됩니다.

2.7 데이터 환경

이 섹션에서는 다음과 같이 병렬 영역을 실행하는 동안 데이터 환경을 제어하기 위한 지시문과 여러 절을 제공합니다.

  • 파일 범위, 네임스페이스 범위 또는 정적 블록 범위 변수를 스레드에 로컬로 만들기 위해 threadprivate 지시문이 제공됩니다.

  • 병렬 또는 작업 공유 구문 기간 동안 변수의 공유 특성을 제어하기 위해 지시문에 지정할 수 있는 절은 섹션 2.7.2에 설명되어 있습니다.

2.7.1 threadprivate 지시문

threadprivate 지시문은 variable-list에 지정된 명명된 파일 범위, 네임스페이스 범위 또는 정적 블록 범위 변수를 스레드에 프라이빗으로 만듭니다. variable-list는 불완전한 형식이 없는 쉼표로 구분된 variable-list입니다. threadprivate 지시문의 구문은 다음과 같습니다.

#pragma omp threadprivate(variable-list) new-line

threadprivate 변수의 각 복사본은 해당 복사본을 처음 참조하기 전 프로그램의 지정되지 않은 지점에서 일반적인 방식으로 한 번 초기화됩니다(예: 마스터 복사본은 프로그램의 직렬 실행에서 초기화됨). 개체가 threadprivate 변수의 명시적 이니셜라이저에서 참조되고 변수 복사본을 처음 참조하기 전에 개체 값이 수정되면 동작이 지정되지 않습니다.

모든 프라이빗 변수와 마찬가지로 스레드는 threadprivate 개체의 다른 스레드 복사본을 참조해서는 안 됩니다. 프로그램의 직렬 영역 및 마스터 영역 중에 참조는 마스터 스레드의 개체 복사본에 대한 것입니다.

첫 번째 병렬 영역이 실행된 후 threadprivate 개체의 데이터는 동적 스레드 메커니즘이 사용하지 않도록 설정되고 모든 병렬 영역에 대해 스레드 수가 변경되지 않은 경우에만 유지됩니다.

threadprivate 지시문에 대한 제한 사항은 다음과 같습니다.

  • 파일 범위 또는 네임스페이스 범위 변수에 대한 threadprivate 지시문은 모든 정의 또는 선언 외부에 나타나야 하며 해당 목록에 있는 모든 변수에 대한 모든 참조 앞에 어휘적으로 나와야 합니다.

  • 파일 또는 네임스페이스 범위에 있는 threadprivate 지시문의 variable-list에 있는 각 변수는 지시문 앞에 어휘적으로 오는 파일 또는 네임스페이스 범위에서 변수 선언을 참조해야 합니다.

  • 정적 블록 범위 변수에 대한 threadprivate 지시문은 중첩된 범위가 아닌 변수 범위에 나타나야 합니다. 지시문은 해당 목록의 변수에 대한 모든 참조보다 어휘적으로 앞에 있어야 합니다.

  • 블록 범위에 있는 threadprivate 지시문의 variable-list에 있는 각 변수는 지시문 앞에 어휘적으로 오는 동일한 범위의 변수 선언을 참조해야 합니다. 변수 선언에서는 정적 스토리지 클래스 지정자를 사용해야 합니다.

  • 하나의 변환 단위에서 threadprivate 지시문에 변수가 지정된 경우 해당 변수가 선언된 모든 변환 단위의 threadprivate 지시문에 지정되어야 합니다.

  • threadprivate 변수는 copyin, copyprivate, schedule, num_threads 또는 if 절을 제외한 어떤 절에도 나타나서는 안 됩니다.

  • threadprivate 변수의 주소는 주소 상수가 아닙니다.

  • threadprivate 변수에는 불완전한 형식이나 참조 형식이 있어서는 안 됩니다.

  • POD 클래스 형식이 아닌 threadprivate 변수는 명시적 이니셜라이저를 사용하여 선언된 경우 액세스 가능하고 명확한 복사 생성자가 있어야 합니다.

다음 예에서는 이니셜라이저에 나타나는 변수를 수정하면 지정되지 않은 동작이 발생할 수 있는 방법과 보조 개체 및 복사 생성자를 사용하여 이 문제를 방지하는 방법을 보여 줍니다.

int x = 1;
T a(x);
const T b_aux(x); /* Capture value of x = 1 */
T b(b_aux);
#pragma omp threadprivate(a, b)

void f(int n) {
   x++;
   #pragma omp parallel for
   /* In each thread:
   * Object a is constructed from x (with value 1 or 2?)
   * Object b is copy-constructed from b_aux
   */
   for (int i=0; i<n; i++) {
      g(a, b); /* Value of a is unspecified. */
   }
}

상호 참조

2.7.2 데이터 공유 특성 절

여러 지시문은 사용자가 해당 지역의 기간 동안 변수의 공유 특성을 제어할 수 있도록 하는 절을 허용합니다. 공유 특성 절은 해당 절이 나타나는 지시문의 어휘 범위에 있는 변수에만 적용됩니다. 모든 지시문에 다음 절이 모두 허용되는 것은 아닙니다. 특정 지시문에 유효한 절 목록은 지시문과 함께 설명됩니다.

병렬 또는 작업 공유 구문이 발견될 때 변수가 표시되고 변수가 공유 특성 절이나 threadprivate 지시문에 지정되지 않은 경우 변수가 공유됩니다. 병렬 영역의 동적 범위 내에서 선언된 정적 변수는 공유됩니다. 힙 할당 메모리(예: C 또는 C++에서 malloc() 사용 또는 C++에서 new 연산자 사용)는 공유됩니다. (그러나 이 메모리에 대한 포인터는 프라이빗이거나 공유될 수 있습니다.) 병렬 영역의 동적 범위 내에서 자동 저장 기간이 선언된 변수는 프라이빗입니다.

대부분의 절은 표시되는 변수의 쉼표로 구분된 목록인 variable-list 인수를 허용합니다. 데이터 공유 특성 절에서 참조된 변수에 템플릿에서 파생된 형식이 있고 프로그램에 해당 변수에 대한 다른 참조가 없는 경우 동작이 정의되지 않습니다.

지시문 절 내에 나타나는 모든 변수는 표시되어야 합니다. 절은 필요에 따라 반복될 수 있지만 변수는 firstprivatelastprivate 절 모두에 지정될 수 있다는 점을 제외하고 둘 이상의 절에 변수를 지정할 수 없습니다.

다음 섹션에서는 데이터 공유 특성 절에 대해 설명합니다.

2.7.2.1 private

private 절은 variable-list의 변수가 팀의 각 스레드에 프라이빗으로 선언됩니다. private 절의 구문은 다음과 같습니다.

private(variable-list)

private 절에 지정된 변수의 동작은 다음과 같습니다. 자동 저장 기간이 있는 새 개체가 구문에 할당됩니다. 새 개체의 크기와 맞춤은 변수 형식에 따라 결정됩니다. 이 할당은 팀의 각 스레드에 대해 한 번 발생하며 필요한 경우 클래스 개체에 대해 기본 생성자가 호출됩니다. 그렇지 않으면 초기값이 불확실합니다. 변수에 의해 참조되는 원래 개체는 구문에 들어갈 때 불확실한 값을 갖고, 구문의 동적 범위 내에서 수정되어서는 안 되며, 구문에서 나갈 때 미확정한 값을 갖습니다.

지시문 구문의 어휘 범위에서 변수는 스레드에 의해 할당된 새로운 프라이빗 개체를 참조하세요.

private 절에 대한 제한 사항은 다음과 같습니다.

  • private 절에 지정된 클래스 형식의 변수에는 액세스 가능하고 명확한 기본 생성자가 있어야 합니다.

  • private 절에 지정된 변수에는 mutable 멤버가 있는 클래스 형식이 없으면 const 한정 형식이 있어서는 안 됩니다.

  • private 절에 지정된 변수에는 불완전한 형식이나 참조 형식이 있어서는 안 됩니다.

  • parallel 지시문의 reduction 절에 나타나는 변수는 병렬 구문에 바인딩되는 작업 공유 지시문의 private 절에 지정할 수 없습니다.

2.7.2.2 firstprivate

firstprivate 절은 private 절에서 제공하는 기능의 상위 집합을 제공합니다. firstprivate 절의 구문은 다음과 같습니다.

firstprivate(variable-list)

variable-list에 지정된 변수는 섹션 2.7.2.1에 설명된 대로 private 절 의미 체계를 갖습니다. 초기화 또는 생성은 스레드가 생성을 실행하기 전에 스레드당 한 번 수행되는 것처럼 발생합니다. 병렬 구문의 firstprivate 절의 경우 새 전용 개체의 초기 값은 이를 발견한 스레드에 대한 병렬 구문 바로 앞에 존재하는 원래 개체의 값입니다. 작업 공유 구문의 firstprivate 절의 경우 작업 공유 구문을 실행하는 각 스레드에 대한 새 전용 개체의 초기 값은 동일한 스레드가 실행되는 특정 시점 이전에 존재하는 원래 개체의 값입니다. 작업 공유 구문을 만나게 됩니다. 또한 C++ 개체의 경우 각 스레드의 새 전용 개체는 원본 개체에서 복사되어 생성됩니다.

firstprivate 절에 대한 제한 사항은 다음과 같습니다.

  • firstprivate 절에 지정된 변수에는 불완전한 형식이나 참조 형식이 있어서는 안 됩니다.

  • firstprivate으로 지정된 클래스 형식의 변수에는 액세스 가능하고 명확한 복사 생성자가 있어야 합니다.

  • 병렬 영역 내에서 프라이빗이거나 parallel 지시문의 reduction 절에 나타나는 변수는 병렬 구문에 바인딩되는 작업 공유 지시문의 firstprivate 절에 지정할 수 없습니다.

2.7.2.3 lastprivate

lastprivate 절은 private 절에서 제공하는 기능의 상위 집합을 제공합니다. lastprivate 절의 구문은 다음과 같습니다.

lastprivate(variable-list)

variable-list에 지정된 변수에는 private 절 의미 체계가 있습니다. 작업 공유 구문을 식별하는 지시문에 lastprivate 절이 나타나면 연결된 루프의 순차적 마지막 반복 또는 어휘적으로 마지막 섹션 지시문의 각 lastprivate 변수 값이 변수의 원래 개체에 할당됩니다. for 또는 parallel for의 마지막 반복이나 sections 또는 parallel sections 지시문의 어휘 마지막 섹션에 의해 값이 할당되지 않은 변수는 구문 뒤에 미확정한 값을 갖습니다. 할당되지 않은 하위 개체도 구문 후에 미확정한 값을 갖습니다.

lastprivate 절에 대한 제한 사항은 다음과 같습니다.

  • private에 대한 모든 제한 사항이 적용됩니다.

  • lastprivate으로 지정된 클래스 형식의 변수에는 액세스 가능하고 명확한 복사 대입 연산자가 있어야 합니다.

  • 병렬 영역 내에서 프라이빗이거나 parallel 지시문의 reduction 절에 나타나는 변수는 병렬 구문에 바인딩되는 작업 공유 지시문의 lastprivate 절에 지정할 수 없습니다.

2.7.2.4 공유됨

이 절은 팀의 모든 스레드 중에서 variable-list에 나타나는 변수를 공유합니다. 팀 내의 모든 스레드는 shared 변수에 대해 동일한 저장 영역에 액세스합니다.

shared 절의 구문은 다음과 같습니다.

shared(variable-list)

2.7.2.5 기본값

default 절을 사용하면 사용자가 변수의 데이터 공유 특성에 영향을 줄 수 있습니다. default 절의 구문은 다음과 같습니다.

default(shared | none)

default(shared)를 지정하는 것은 threadprivate 또는 const 한정이 아닌 한 shared 절에 현재 표시되는 각 변수를 명시적으로 나열하는 것과 같습니다. 명시적인 default 절이 없으면 기본 동작은 default(shared)가 지정된 것과 동일합니다.

default(none)을 지정하려면 병렬 구문의 어휘 범위에 있는 변수에 대한 모든 참조에 대해 다음 중 하나 이상이 참이어야 합니다.

  • 변수는 참조를 포함하는 구문의 데이터 공유 특성 절에 명시적으로 나열됩니다.

  • 변수는 병렬 구문 내에서 선언됩니다.

  • 변수는 threadprivate입니다.

  • 변수에 const 한정 형식이 있습니다.

  • 변수는 for 또는 parallel for 지시문 바로 뒤에 오는 for 루프에 대한 루프 제어 변수이며 변수 참조는 루프 내부에 나타납니다.

포함된 지시문의 firstprivate, lastprivate 또는 reduction 절에 변수를 지정하면 둘러싸는 컨텍스트에서 변수에 대한 암시적 참조가 발생합니다. 이러한 암시적 참조에는 위에 나열된 요구 사항도 적용됩니다.

parallel 지시문에는 단일 default 절만 지정할 수 있습니다.

변수의 기본 데이터 공유 특성은 다음 예와 같이 private, firstprivate, lastprivate, reductionshared 절을 사용하여 재정의할 수 있습니다.

#pragma  omp  parallel  for  default(shared)  firstprivate(i)\
   private(x)  private(r)  lastprivate(i)

2.7.2.6 reduction

이 절은 op 연산자를 사용하여 variable-list에 나타나는 스칼라 변수에 대한 축소를 수행합니다. reduction 절의 구문은 다음과 같습니다.

reduction( op : variable-list )

감소는 일반적으로 다음 형식 중 하나를 사용하여 문에 대해 지정됩니다.

  • x = x op expr
  • x binop = expr
  • x = expr op x(뺄셈 제외)
  • x ++
  • ++ x
  • x --
  • -- x

여기서

x
목록에 지정된 감소 변수 중 하나입니다.

variable-list
스칼라 감소 변수의 쉼표로 구분된 목록입니다.

expr
x를 참조하지 않는 스칼라 형식의 식입니다.

op
오버로드된 연산자가 아니라 +, *, -, &, ^, |, && 또는 || 중 하나입니다.

binop
오버로드된 연산자가 아니라 +, *, -, &, ^ 또는 | 중 하나입니다.

다음은 reduction 절의 예입니다.

#pragma omp parallel for reduction(+: a, y) reduction(||: am)
for (i=0; i<n; i++) {
   a += b[i];
   y = sum(y, c[i]);
   am = am || b[i] == c[i];
}

예에서 볼 수 있듯이 연산자는 함수 호출 내부에 숨겨져 있을 수 있습니다. 사용자는 reduction 절에 지정된 연산자가 축소 연산과 일치하는지 주의해야 합니다.

이 예에서는 || 연산자의 오른쪽 피연산자에 부작용이 없지만 허용되지만 주의해서 사용해야 합니다. 이러한 컨텍스트에서 루프의 순차적 실행 중에 발생하지 않는 것이 보장되는 부작용이 병렬 실행 중에 발생할 수 있습니다. 이러한 차이는 반복 실행 순서가 불확실하기 때문에 발생할 수 있습니다.

연산자는 컴파일러에서 축소를 위해 사용하는 모든 프라이빗 변수의 초기 값을 결정하고 종료 연산자를 결정하는 데 사용됩니다. 연산자를 명시적으로 지정하면 축소 문이 구문의 어휘 범위 외부에 있을 수 있습니다. 지시문에는 여러 개의 reduction 절을 지정할 수 있지만 변수는 해당 지시문에 대해 최대 하나의 reduction 절에 나타날 수 있습니다.

private 절이 사용된 것처럼 variable-list에 있는 각 변수의 프라이빗 복사본이 각 스레드마다 하나씩 만들어집니다. 프라이빗 복사본은 연산자에 따라 초기화됩니다(다음 표 참조).

reduction 절이 지정된 영역의 끝에서 원래 개체는 지정된 연산자를 사용하여 원래 값과 각 프라이빗 복사본의 최종 값을 결합한 결과를 반영하도록 업데이트됩니다. 축소 연산자는 모두 결합적(뺄셈 제외)이며 컴파일러는 최종 값 계산을 자유롭게 재연관할 수 있습니다. (뺄셈의 일부 결과를 더해 최종 값을 형성합니다.)

첫 번째 스레드가 포함 절에 도달하면 원래 개체의 값은 불확실해지고 축소 계산이 완료될 때까지 그대로 유지됩니다. 일반적으로 계산은 구문이 끝나면 완료됩니다. 그러나 reduction 절이 nowait도 적용되는 구문에 사용되는 경우 모든 스레드가 reduction 절을 완료했는지 확인하기 위해 장벽 동기화가 수행될 때까지 원래 개체의 값은 미확정 상태로 유지됩니다.

다음 표에는 유효한 연산자와 해당 정식 초기화 값이 나열되어 있습니다. 실제 초기화 값은 감소 변수의 데이터 형식과 일치합니다.

연산자 초기화
+ 0
* 1
- 0
& ~0
| 0
^ 0
&& 1
|| 0

reduction 절에 대한 제한 사항은 다음과 같습니다.

  • reduction 절의 변수 형식은 포인터 형식과 참조 형식이 허용되지 않는다는 점을 제외하고 축소 연산자에 대해 유효해야 합니다.

  • reduction 절에 지정된 변수는 const 정규화되어서는 안 됩니다.

  • 병렬 영역 내에서 프라이빗이거나 parallel 지시문의 reduction 절에 나타나는 변수는 병렬 구문에 바인딩되는 작업 공유 지시문의 reduction 절에 지정할 수 없습니다.

    #pragma omp parallel private(y)
    { /* ERROR - private variable y cannot be specified
                  in a reduction clause */
        #pragma omp for reduction(+: y)
        for (i=0; i<n; i++)
           y += b[i];
    }
    
    /* ERROR - variable x cannot be specified in both
                a shared and a reduction clause */
    #pragma omp parallel for shared(x) reduction(+: x)
    

2.7.2.7 copyin

copyin 절은 병렬 영역을 실행하는 팀의 각 스레드에 대해 threadprivate 변수에 동일한 값을 할당하는 메커니즘을 제공합니다. copyin 절에 지정된 각 변수에 대해 팀의 마스터 스레드에 있는 변수 값은 마치 할당에 의한 것처럼 병렬 영역 시작 부분에 있는 스레드 전용 복사본에 복사됩니다. copyin 절의 구문은 다음과 같습니다.

copyin(
variable-list
)

copyin 절에 대한 제한 사항은 다음과 같습니다.

  • copyin 절에 지정된 변수에는 액세스 가능하고 명확한 복사 대입 연산자가 있어야 합니다.

  • copyin 절에 지정된 변수는 threadprivate 변수여야 합니다.

2.7.2.8 copyprivate

copyprivate 절은 프라이빗 변수를 사용하여 팀의 한 멤버에서 다른 멤버로 값을 브로드캐스트하는 메커니즘을 제공합니다. 이러한 공유 변수를 제공하는 것이 어려울 때(예: 각 수준에서 다른 변수가 필요한 재귀의 경우) 값에 대해 공유 변수를 사용하는 것의 대안입니다. copyprivate 절은 single 지시문에만 나타날 수 있습니다.

copyprivate 절의 구문은 다음과 같습니다.

copyprivate(
variable-list
)

variable-list의 변수에 대한 copyprivate 절의 효과는 single 구문과 연관된 구조화된 블록을 실행한 후 팀의 스레드 중 하나가 구성 종료 시 장벽을 떠나기 전에 발생합니다. 그런 다음 팀의 다른 모든 스레드에서 variable-list의 각 변수에 대해 해당 변수는 구문의 구조화된 블록을 실행한 스레드의 해당 변수 값으로 정의됩니다(할당에 의해).

copyprivate 절에 대한 제한 사항은 다음과 같습니다.

  • copyprivate 절에 지정된 변수는 동일한 single 지시문에 대한 private 또는 firstprivate 절에 나타나서는 안 됩니다.

  • copyprivate 절이 있는 single 지시문이 병렬 영역의 동적 범위에서 발견되면 copyprivate 절에 지정된 모든 변수는 둘러싸는 컨텍스트에서 프라이빗이어야 합니다.

  • copyprivate 절에 지정된 변수에는 액세스 가능한 명확한 복사 대입 연산자가 있어야 합니다.

2.8 지시문 바인딩

지시문의 동적 바인딩은 다음 규칙을 준수해야 합니다.

  • for, sections, single, masterbarrier 지시문은 해당 지시어에 존재할 수 있는 if 절의 값에 관계없이 동적으로 둘러싸는 parallel(있는 경우)에 바인딩됩니다. 현재 실행 중인 병렬 영역이 없으면 마스터 스레드로만 구성된 팀에서 지시문을 실행합니다.

  • ordered 지시문은 동적으로 둘러싸는 for에 바인딩됩니다.

  • atomic 지시문은 현재 팀뿐만 아니라 모든 스레드의 atomic 지시문과 관련하여 배타적 액세스를 적용합니다.

  • critical 지시문은 현재 팀뿐만 아니라 모든 스레드의 critical 지시문과 관련하여 배타적 액세스를 적용합니다.

  • 지시문은 가장 가깝게 동적으로 둘러싸는 parallel 외부의 지시문에 바인딩할 수 없습니다.

2.9 지시문 중첩

지시문의 동적 중첩은 다음 규칙을 준수해야 합니다.

  • 다른 parallel 내부의 parallel 지시문은 중첩된 병렬 처리가 사용하도록 설정되지 않은 한 현재 스레드로만 구성된 새 팀을 논리적으로 설정합니다.

  • 동일한 parallel에 바인딩되는 for, sectionssingle 지시문은 서로 중첩될 수 없습니다.

  • 동일한 이름을 가진 critical 지시문은 서로 중첩될 수 없습니다. 이 제한은 교착 상태를 방지하는 데 충분하지 않습니다.

  • for, sectionssingle 지시문이 지역과 동일한 parallel에 바인딩되는 경우 critical, orderedmaster 지역의 동적 범위에서는 허용되지 않습니다.

  • barrier 지시문이 동일한 parallel에 지역으로 바인딩되는 경우 for, ordered, sections, single, mastercritical 지역의 동적 범위에서는 허용되지 않습니다.

  • master 지시문이 작업 공유 지시문과 동일한 parallel에 바인딩되는 경우 master 지시문은 for, sectionssingle 지시문의 동적 범위에서 허용되지 않습니다.

  • 지시문이 지역과 동일한 parallel에 바인딩되는 경우 ordered 지시문은 critical 지역의 동적 범위에서 허용되지 않습니다.

  • 병렬 영역 내에서 동적으로 실행될 때 허용되는 모든 지시문은 병렬 영역 외부에서 실행될 때도 허용됩니다. 사용자가 지정한 병렬 영역 외부에서 동적으로 실행되는 경우 지시문은 마스터 스레드로만 구성된 팀에 의해 실행됩니다.