메뉴 닫기

OpenMP 사용하기

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를 사용여부를 결정하시기 바랍니다 🙂

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다