2007년 11월 23일 금요일

OpenMP

OpenMP는 다양한 플랫폼에서 C/C++/Fortran에서 병렬처리를 손쉽게 해주는 API일종이다. 공식 홈페이지는 http://www.openmp.org이다.

뭔말인고 하니, 병렬처리 프로그래밍하려면 UNIX에서 fork/ipc나 pthread 등등 다양한 API에 대해 공부해야하고, 이것을 Windows로 옮길 경우, Win32API로 소스를 뜯어 고쳐야하는 불상사가 있었다. 이걸 해결하고자 OpenMP 표준을 지키는 컴파일러에서 간단하게 thread를 생성, 운용할 수 있도록 통합한 API이다. 게다가 잘만 운용하면 기존 소스를 거의 고칠 필요가 없다는 것! OpenMP는 현재 2.5(Draft는 3.0)까지 나왔으며, GCC에서 지원하는 것도 2.5까지다. 컴파일할 때 -fopenmp를 해주면 "#pragma omp" 구문을 알아서 "omp_*" 함수로 변환해준다. 아래는 간단한 예제이며, 돌려본 결과를 미리 이야기 하자면, 66.7% 이득을 봤다. 나름 쓸모 있는 녀석이긴 하지만, 제어할 수 있는 부분이 너무 적어서 과연 과학적 연산을 제외하고 일반적인 상품(게임 클라이언트)에 쓸모가 있을런지는 모르겠다.

테스트 환경
Linux 2.6.23
AMD Athlon 64 X2 Dual Core 3800+
RAM 2G
GCC 4.1.2
GLIBC 2.7
CMAKE 2.4
먼저 소스 파일(openmp.c)
#include <pthread.h>
#include <sys/time.h>
#include <omp.h>
#include <stdio.h>

#define __restrict__ restrict

static const size_t gTestCount = 10000000;
static const size_t gArraySize = 32*32;

long long
getTimestamp(void)
{
    static __thread struct timeval tv;
    gettimeofday(&tv, NULL);
    return 1000000LL * tv.tv_sec + tv.tv_usec;
}

static
inline
void
clean(double* restrict a, double* restrict b)
{
    static __thread size_t i = 0;
    while ( i < gArraySize )
    {
        a[i] = i;
        b[i] = -i;
        ++i;
    }
}

static
inline
void
alu(double* restrict a, const double* restrict b)
{
    static __thread size_t i = 0;
    while ( i < gArraySize )
    {
        a[i] = a[i]*b[i];
        ++i;
    }
}

int
main(int argc,char* argv[])
{
    double a[gArraySize], b[gArraySize];
    size_t c = 0;

    long long t1, t2, diff;

    clean(a,b);
    t1 = getTimestamp();
    for ( c = 0; c < gTestCount; c++ )
    {
        alu(a,b);
    }
    t2 = getTimestamp();
    diff = t2-t1;
    printf("Serial: %lld\n", diff);

    clean(a,b);
    omp_set_num_threads(4);
    t1 = getTimestamp();
#   pragma omp parallel for
    for ( c = 0; c < gTestCount; c++ )
    {
        alu(a,b);
    }
    t2 = getTimestamp();
    diff = t2-t1;
    printf("Parallel: %lld\n", diff);

    return 0;
}

빌드 파일(CMakeList.txt)
add_definitions(-DNDEBUG -D_REENTRANT)
add_executable(openmp openmp.c)
target_link_libraries(openmp pthread)
set_target_properties(openmp
    PROPERTIES
    LINK_FLAGS "${CMAKE_LINK_FLAGS} -fopenmp -Wall -g -O3"
    COMPILE_FLAGS "${CMAKE_COMPILE_FLAGS} -std=c99 -fopenmp -O3 -ftree-vectorize")
실행 결과
$ cmake .
-- Configuring done
-- Generating done
-- Build files have been written to: /home/daldev/purewell/tmp/openmp

$ make
[100%] Building C object CMakeFiles/openmp.dir/openmp.o
/home/daldev/purewell/tmp/openmp/openmp.c: In function ‘main’:
/home/daldev/purewell/tmp/openmp/openmp.c:68: warning: iteration variable ‘c’ is unsigned
Linking C executable openmp
[100%] Built target openmp

$ ./openmp
Serial: 30121
Parallel: 10328


Powered by ScribeFire.