OpenMP는 Open Multi-Processing의 약자로 공유 메모리 멀티프로세싱 프로그래밍을 지원하기 위한 API입니다. 영상 처리 시, 간혹 대규모의 반복문을 처리할 때 사용합니다. 요즘은 워낙 HW 성능이 좋아 처리속도가 빠르기 때문에, 단순 반복 연산에서는 openmp로 눈에 띄는 효과를 보기는 어렵습니다. 단일 루프에 15ms 미만으로 실행되는 경우에는 굳이 쓸 필요가 없다고 합니다.
Visual studio에서 OpenMP 사용하기
Visual studio에서는 어떻게 사용하면 될지 알아보도록 하겠습니다. 프로젝트의 속성 페이지(ALT+ENTER)에 들어갑니다. 구성속성 → C/C++ → 언어 → OpenMP 지원에서 예(/openmp)를 선택합니다.
※ 빌드 중에 “warning C1499: C++/CLI, C++/CXX 또는 OpenMP에서는 2단계 이름 조회가 지원되지 않습니다. /Zc:twoPhase-를 사용하세요.” 라는 문구가 뜰 경우, 구성속성 → C/C++ → 명령줄의 추가 옵션 입력창에 /Zc:twoPhase- 를 입력하시기 바랍니다.
OpenMP 사용법 알아보기
몇 가지 중요한 지시문에 대해서 언급하도록 하겠습니다.
원자적으로 업데이트될 메모리 위치를 지정하여, 두 개 이상의 쓰기 접근으로부터 메모리를 보호합니다.
#pragma omp atomic
expression (한 줄)
int count = 0;
#pragma omp parallel num_threads(MAX)
{
#pragma omp atomic
count++;
}
코드 블록이 한 번에 하나의 쓰레드에서만 실행되도록 합니다. 예를 들어 각 쓰레드에서 특정 변수에 값을 계속 더해서 대입할 때, 여러 쓰레드에서 동시에 접근을 하게 되면 정확한 연산 결과를 얻을 수 없습니다. 이럴 때 단독 실행을 보장하도록 critical 지시문을 사용합니다.
#pragma omp critical [(name)]
{
코드 블록
}
int i=0;
int count = 0;
int SIZE = 100000;
#pragma omp parallel for num_threads(4)
for (i=0 ; i<SIZE ; i++)
{
#pragma omp critical
{
if (i % 2 == 0)
count++;
}
}
병렬 영역을 지정합니다.
#pragma omp parallel [clauses]
{
코드 블록
}
unsigned long long int count = 0;
#pragma omp parallel num_threads(4)
{
#pragma omp for
for (int i = 0; i < 100001; i++)
{
#pragma omp atomic
count += i;
}
}
for 루프 작업을 쓰레드 개수만큼 분할하여 수행합니다.
#pragma omp parallel for [clauses]
for문
unsigned long long int total = 0;
#pragma omp parallel for num_threads(4)
for (int i = 0; i < 100001; i++)
{
#pragma omp critical
{
total += i;
}
}
clauses에는 private, firstprivate, lastprivate, reduction 등이 들어갈 수 있습니다.
- private: 선언된 변수를 복사하여 쓰레드의 지역 변수로 사용하도록 합니다. 초기화는 되어 있지 않습니다.
- firstprivate: 선언된 변수를 값과 함께 복사하여 쓰레드의 지역 변수로 사용하도록 합니다.
OpenMP 사용 예제
아래와 같이 간단한 예제 코드를 준비하였습니다.
count 함수는 1부터 limit 파라미터(여기서는 10,000,000)까지 1씩 증가시키는 코드이며, 2번째 파라미터로 threads 의 개수를 지정할 수 있습니다. 반복문 내에는 현재 쓰레드 넘버를 확인하기 위한 코드도 추가되어 있는데, 필요할 경우 주석을 풀고 확인할 수 있습니다. 함수 동작 시간은 GetTickCount() 를 사용하여 측정합니다.
#include <stdio.h>
#include <windows.h>
#include <math.h>
#include <omp.h>
unsigned long long count(int limit, int threads)
{
unsigned long long total = 0;
int step = (int)sqrt(limit);
printf_s("%d %d %dn", limit, threads, step);
#pragma omp parallel for num_threads(threads)
for (int i = 0; i < limit; i++)
{
#pragma omp critical
{
total += 1;
}
//if (i%step == 0)
//{
// printf_s("thread ID: %dn", omp_get_thread_num());
//}
}
return total;
}
int main()
{
int limit = 10000000;
DWORD dwStart = GetTickCount();
unsigned long long res = count(limit, 8);
printf_s("%lld - %d millisecondn", res, GetTickCount()-dwStart);
return 0;
}
이렇게 간단한 예제에서는 사실 openmp를 안쓰느니만 못한 결과가 나옵니다. 코드 상황을 봐가면서 openmp를 사용여부를 결정하시기 바랍니다 🙂