
매일 GPU나 분산 컴퓨팅으로 병렬 처리만 하다가, 오랜만에 단일 cpu환경에서 thread로 병렬 처리를 해야 할 일이 생겼다. 학습하는 김에 주요 사용법을 정리해보고자 한다.
개발 환경
- C/C++
- OpenMP 5.0 API
사용법
1. omp parallel
기본 사용법
#pragma omp parallel
//병렬 처리 할 한줄
#pragma omp parallel
{
/*
병렬 처리 할 여러줄
*/
}
다음 한 줄이나 다음에 오는 중괄호에 묶인 구문을 병렬로 실행한다. thread 수만큼 실행된다.
#pragma omp parallel | |
printf("thread num %d : first hello world\n", omp_get_thread_num()); // thread 개수만큼 실행 | |
printf("thread num %d : second hello world\n", omp_get_thread_num()); // 한번만 실행 | |
return 0; | |
/* | |
thread num 0 : first hello world | |
thread num 2 : first hello world | |
thread num 1 : first hello world | |
thread num 3 : first hello world | |
thread num 0 : second hello world | |
*/ |
#pragma omp parallel | |
{ | |
printf("thread num %d : first hello world\n", omp_get_thread_num()); // thread 개수만큼 실행 | |
printf("thread num %d : second hello world\n", omp_get_thread_num()); // thread 개수만큼 실행 | |
} | |
/* | |
thread num 0 : first hello world | |
thread num 0 : second hello world | |
thread num 3 : first hello world | |
thread num 3 : second hello world | |
thread num 1 : first hello world | |
thread num 1 : second hello world | |
thread num 2 : first hello world | |
thread num 2 : second hello world | |
*/ |
옵션
#pragma omp parallel if(condition)
#pragma omp parallel num_threads(n)
#pragma omp parallel private(a)
#pragma omp parallel firstprivate(b)
#pragma omp parallel shared(c)
중복되는 옵션은 한 번만 설명합니다.
if(condition) : 조건이 참일 때만 병렬 처리된다.
bool is_parallel = false; | |
#pragma omp parallel if(is_parallel) // if 문이 false 이므로 병렬처리 되지 않는다. | |
{ | |
printf("thread num %d : first hello world\n", omp_get_thread_num()); | |
printf("thread num %d : second hello world\n", omp_get_thread_num()); | |
} |
num_threads(n) : n개의 thread로만 병렬 처리한다.
int n = 2; | |
#pragma omp parallel num_threads(n) | |
{ | |
printf("thread %d\n", omp_get_thread_num()); // n개의 thread가 각각 1번씩 실행 | |
} | |
/* | |
thread 0 | |
thread 1 | |
*/ |
private(a) : 변수 a를 각각 copy 해서 사용한다. parallel region 이전의 초기화가 무의미하다.
firstprivate(b) : 변수 b를 각각 copy 해서 사용한다. parallel region 이전 값이 유지된다.
int a = 1234; | |
#pragma omp parallel private(a) num_threads(2) | |
{ | |
printf("private 1 : %d\n", a); // 초기화 되지 않는다. dummy 출력 | |
} | |
#pragma omp parallel private(a) num_threads(2) | |
{ | |
a = 123; | |
printf("private 2 : %d\n", a); // 내부 초기화. 정상값 출력 | |
} | |
#pragma omp parallel firstprivate(a) num_threads(2) | |
{ | |
printf("private 3 : %d\n", a); // 외부 초기화. 정상값 출력 | |
} | |
/* | |
private 1 : 0 | |
private 1 : 32510 | |
private 2 : 123 | |
private 2 : 123 | |
private 3 : 1234 | |
private 3 : 1234 | |
*/ |
shared(c) : 변수 c를 모든 thread가 하나의 copy로 사용한다. parallel region 이전의 값이 유지된다.
주의! shared 예제는 자원 동시 접근이 가능해서 따라 하면 안됨. 아래 atomic이나 critical을 참고.
2. omp for
#pragma omp parallel
#pragma omp for
// for-loop
// or
#pragma omp parallel
{
#pragma omp for
// for-loop
}
for 혼자서는 동작하지 않고 parallel 구문 안에 있어야 정상 동작한다.
#pragma omp parallel | |
#pragma omp for | |
for(int i = 0 ;i < 5; i++) | |
{ | |
printf("thread %d : %d (in parallel)\n", omp_get_thread_num(), i); | |
} | |
#pragma omp for | |
for(int i = 0 ;i < 5; i++) | |
{ | |
printf("thread %d : %d (not in parallel)\n", omp_get_thread_num(), i); | |
} | |
/* | |
thread 0 : 0 (in parallel) | |
thread 1 : 1 (in parallel) | |
thread 2 : 2 (in parallel) | |
thread 3 : 3 (in parallel) | |
thread 4 : 4 (in parallel) | |
thread 0 : 0 (not in parallel) | |
thread 0 : 1 (not in parallel) | |
thread 0 : 2 (not in parallel) | |
thread 0 : 3 (not in parallel) | |
thread 0 : 4 (not in parallel) | |
*/ |
3. omp parallel for
#pragram omp parallel for
// for-loop
#pragma omp parallel
#pragma omp for
와 동일하다. 그럼 왜 두 줄로 나눠쓰자? 아마 parallel에만 들어갈 수 있는 옵션 때문에?
4. omp atomic
#pragma omp atomic
// atomic section
연산을 atomic 하게 한다. 공유 변수에 접근할 때 쓴다.
지원되는 연산 종류만 가능하며, 불가능한 경우 critical section으로 대체 가능하다.

int sum; | |
sum = 0; | |
#pragma omp parallel for | |
for(int i = 0 ;i < 100; i++) | |
#pragma omp atomic | |
sum += i; | |
printf("atomic sum : %d\n", sum); | |
sum = 0; | |
#pragma omp parallel for | |
for(int i = 0 ;i < 100; i++) | |
sum += i; | |
printf("not atomic sum : %d\n", sum); | |
/* | |
atomic sum : 4950 | |
not atomic sum : 2071 | |
*/ |
5. omp critical
#pragma omp critical
// critical section
충동할 수 있는 자원에 대해서 critical section으로 설정한다. atomic보다 더 오래 걸리니 atomic이 불가능할 때 사용하자.
std::vector<int> v; | |
#pragma omp parallel num_threads(4) | |
{ | |
if(omp_get_thread_num() == 0) | |
sleep(1); | |
v.push_back(omp_get_thread_num()); | |
} | |
for(std::vector<int>::iterator it = v.begin() ; it != v.end() ; it++) | |
printf("%d\n", *it); | |
// 세그멘테이션 오류 (core dumped) | |
std::vector<int> v; | |
#pragma omp parallel num_threads(4) | |
{ | |
if(omp_get_thread_num() == 0) | |
sleep(1); | |
#pragma omp critical | |
v.push_back(omp_get_thread_num()); | |
} | |
for(std::vector<int>::iterator it = v.begin() ; it != v.end() ; it++) | |
printf("%d\n", *it); | |
/* | |
1 | |
3 | |
2 | |
0 | |
*/ |
Reference
쓰다 보니 많아져서 다음 편에 이어서...
'Programming > C | C++' 카테고리의 다른 글
STL - Vector 삽입/삭제/정렬/복사 등 사용법 예제 (0) | 2021.03.10 |
---|---|
OpenMP(2) - 입문자용 주요 사용법 간단한 설명/정리(omp single, master, task, taskwait, barrier) (2) | 2020.07.31 |
댓글