의 차이는 첫 번째 블록 당신이 블록 자체가 활성 내부 (도 구문이나 어휘) 중첩되지 않기 때문에 이 정말 어떤 작업을 생성하지 않은 것입니다 평행 영역. 두 번째 블록에서 task
구조는 문법적으로 parallel
영역 내에 중첩되어 있으며 영역이 런타임에 활성 상태 인 경우 명시 적 작업을 대기열에 넣습니다 (활성 병렬 영역은 둘 이상의 스레드로 구성된 팀과 함께 실행됩니다). 사전 적 중첩은 덜 명확합니다. 다음 예를 살펴 :
가
void foo(void)
{
int i;
for (i = 0; i < 10; i++)
#pragma omp task
bar();
}
int main(void)
{
foo();
#pragma omp parallel num_threads(4)
{
#pragma omp single
foo();
}
return 0;
}
foo()
처음 호출 모든 병렬 영역 밖에서 일어난다. 따라서 task
지시문은 (거의) 아무것도 수행하지 않고 bar()
에 대한 모든 호출이 연속적으로 발생합니다. foo()
에 대한 두 번째 호출은 병렬 영역 내부에서 이루어 지므로 foo()
내부에 새 작업이 생성됩니다. parallel
영역은 스레드 수가 4
으로 고정되었으므로 num_threads(4)
절에 의해 활성화됩니다.
OpenMP 지시문의 이러한 다른 동작은 디자인 기능입니다. 주요 아이디어는 직렬 및 병렬로 실행할 수있는 코드를 작성하는 것입니다.
여전히 foo()
에 task
구조체가 존재하면 코드 변환이 약간 수행된다. 여기 OMP_make_task()
void foo_omp_fn_1(void *omp_data)
{
bar();
}
void foo(void)
{
int i;
for (i = 0; i < 10; i++)
OMP_make_task(foo_omp_fn_1, NULL);
}
는 첫 번째 인수로 제공되는 함수에 대한 호출을 큐잉 OpenMP를 지원 라이브러리에서 가상의 (공개되지 않음) 함수 : foo()
은 같은 것으로 변환된다. OMP_make_task()
이 감지되면 액티브 병렬 영역 외부에서 작동하지만 대신 foo_omp_fn_1()
을 호출합니다. 이로 인해 일련의 경우 bar()
호출에 약간의 오버 헤드가 추가됩니다. main -> foo -> bar
대신 main -> foo -> OMP_make_task -> foo_omp_fn_1 -> bar
과 같이 호출됩니다. 이것은 직렬 코드 실행 속도가 느리다는 것을 의미합니다.
이는 더욱 분명 작업 공유 지시자로 설명된다
void foo(void)
{
int i;
#pragma omp for
for (i = 0; i < 12; i++)
bar();
}
int main(void)
{
foo();
#pragma omp parallel num_threads(4)
{
foo();
}
return 0;
}
foo()
에 대한 첫 번째 호출은 일련의 루프를 실행됩니다. 제 2 호출은 4 개의 스레드 사이에 12 개의 반복을 분배 할 것이다. 즉, 각 스레드는 3 개의 반복만을 실행할 것이다. 다시 한번,이를 달성하기 위해 일부 코드 변환 마법이 사용되며 이 존재하지 않는 경우보다 foo()
에 직렬 루프가 느리게 실행됩니다.
여기서 교훈은 실제로 필요하지 않은 OpenMP 구문을 절대 추가하지 않는 것입니다.
+1 좋은 답변입니다. – dreamcrash
나는 작업의 사용에 실수를 한 것으로 보인다.이 문제는 내가 질문에서 추가 한대로 단지 "작업"으로 트리를 재귀 적으로 가로 지르는 코드를 보았 기 때문에 발생했습니다. 나는 그것이 호출되는 곳에서 "병렬"과 "단일"이 트래버스 기능을 둘러싸도록해야한다고 생각합니다. 진심으로 감사드립니다. –
@AnnieKim, 예, 질문에 표시된'traverse()'함수는 활성'parallel' 영역 내에서 호출 된 경우 병렬로 트리를 트래버스하고 그렇지 않은 경우 연속적으로 트래버스합니다. 그것은 OpenMP의 장점입니다. (오버 헤드가 추가 되었음에도 불구하고) –