89
Thread Programming 최최최

Thread programming

Embed Size (px)

Citation preview

Page 1: Thread programming

Thread Programming최윤종

Page 2: Thread programming

Advanced! 리눅스 네트워크 시스템 프로그래밍 김선영 저 , 2 판 가메출판사

책의 8 장 스레드 프로그래밍

출처

Page 3: Thread programming

무임승차했던 성능 문제 멀티스레딩과 성능 향상 병렬 처리 패턴 스레드 안전 pthread OpenMP 프로그래밍 성능을 고려한 프로그래밍

목차

Page 4: Thread programming

무임승차했던 성능 문제

Page 5: Thread programming

무임승차했던 성능 문제 인텔 CPU 의 발전

무어의 법칙◦ 반도체의 회로 집적도는 2 년마다 2 배씩 증가

성능이 떨어지는 코드 하드웨어 교체로 개선

80 년대 초반 90 년대 초반

반도체 미세 공정 능력 0.18 ㎛ 65 ㎚

트랜지스터 집적 능력 약 9.5M 개 약 291M

클록 ( ㎐ ) 500 ㎒ 3.2 ㎓

Page 6: Thread programming

AMD 4 ㎓ CPU◦ 매우 많은 전력 소모와 무자비한 발열

전산업계의 거품 현상◦ 비용 소모 ( 전력 등 ) 는 투자 및 자금 유치로 해결◦ 거품이 빠지면서 공황상태

Green IT, 모바일 시장의 발전◦ 전력 절감만이 살길

프로세서 성능의 둔화

Page 7: Thread programming

I7 입니다 . 내용과 무관합니다

멀티 코어 CPU 로의 진화

Page 8: Thread programming

공정 기술의 향상으로 CPU 에 복수 개의 코어 이식◦ CMP(Chip Multi Processor), 내장 그래픽 등

멀티 코어 CPU 로의 진화

I7 입니다 . 내용과 무관합니다

Page 9: Thread programming

성능이 비슷하거나 낮은 여러 개의 코어◦ 병렬 처리 , 비동기 처리 기법에 대한 연구 진행◦ 단일 스레드 프로그램에서는 성능 저하 유발

멀티 코어 CPU 로의 진화

I7 입니다 . 내용과 무관합니다

Page 10: Thread programming

책에서는 Thread Building Block 소개가 없습니다 pthread(POSIX thread)

◦ 최신 UNIX 커널은 대부분 지원◦ 윈도우 native 스레드에 비해 성능이 떨어집니다

OpenMP◦ 스레드 기법 몇가지를 단순화◦ C/C++ portran 등 다양한 언어 지원◦ #pragma omp 로 직접 함수를 코딩하지 않아도 됩니다◦ 전처리기 수준에서 처리하므로 높은 이식성 확보◦ GCC/MSC 지원

다양한 스레드 프로그래밍

Page 11: Thread programming

멀티 스레딩과 성능 향상

Page 12: Thread programming

단일 CPU 에서의 처리 방법

멀티코어 CPU 에서의 병렬 처리 방법

단일 CPU 에서의 동시 처리 방법

병렬 처리와 동시 처리

Task #1 Task #2

Task #1

Task #2

Task #1

Task #2

Page 13: Thread programming

Latency◦ 요청 후 실제 응답이 오기까지 걸리는 대기 시간◦ CPU 가 다른 장치에 비해 매우 빠르므로 생기는 시간

Latency 중 CPU 에서 다른 스레드의 작업을 처리◦ I/O 를 비동기로 처리하므로 Latency Hiding 가능

Latency Hiding

wait

wait

I/O Request

I/O Response& processing

Page 14: Thread programming

프로그램의 성능 향상을 위한 I/O 및 저장장치 접근 최소화를 위한 두가지 방법

Multi Thread◦ 프로세스 내부 처리 구조를 Multi Process 처럼 분업화◦ 스레드 간 통신에 따른 비용 소모가 거의 없음◦ 스레드 간 동기화 처리 비용 소모

Multi Process◦ 동기적 프로그래밍 모델◦ 보호되는 자원 ( 메모리 , 장치 등 )◦ 프로세스간 통신에 따른 비용 소모 (IPC 등 )

Multi Thread VS Multi Process

Page 15: Thread programming

Multi Process

Multi Thread

Multi Thread VS Multi Process

Process Process

IPCcopy

copy

process

Thread Thread

Data

Page 16: Thread programming

멀티 스레딩은 최후의 보루입니다◦ 개발 , 테스트 , 디버깅이 복잡해집니다◦ 설계나 개발 단계에서 예측하지 못한 상황에 빠질 가능성이

있습니다◦ “ 프로그래머가 관찰을 위해 개입하는 순간 , 비결정적인

결과를 나타내어 버그가 사라지는 현상”

최적화와 멀티 스레딩의 문제점

Page 17: Thread programming

고려 사항◦ I/O 측면에서 병목 (bottleneck) 이 제거된 상태인가 ?◦ 멀티 스레드 도입 전의 코드는 다른 최적화 방법을 적용해

보았는가 ?◦ 멀티 스레드 도입 전의 시스템에 가용할 수 있는 CPU 나 관련

유휴 자원은 충분한가 ?◦ 효율을 극대화하려면 스레드의 숫자를 어떻게 결정해야

하는가 ?

최적화와 멀티 스레딩의 문제점

Page 18: Thread programming

병렬 처리 패턴

Page 19: Thread programming

대분류에 속하는 패턴입니다

태스크 분해 (Task Decomposition)◦ 작업의 단위나 순서의 흐름이 중요한 경우

데이터 분해 (Data Decomposition)◦ 작업할 데이터의 형태나 크기가 중요한 경우

병렬 처리 패턴

Page 20: Thread programming

처리해야 하는 데이터나 행위가 매번 달라지거나 , 연속적인 흐름을 띄는 경우에 적합

콜백 , 트리 /파면 분기 , 파이프라인 등◦웹 브라우저에 적용

병렬 처리를 위해 분해된 태스크들을 스레드가 실행하는 형태◦규모에 따라 태스크 하나에 하위 스레드 여러 개 할당◦ 선후관계 / 의존성에 따른 동기화 처리 요구 ( 배리어 등 )

태스크 분해 방식

Page 21: Thread programming

트리 구조의 태스크

파이프라인 구조의 태스크

태스크 분해 방식

Task

Task

Task

Task

Task

Task

Task

Data DataData Data Data Data Data Data

Task #1 Task #2 Task #3

Page 22: Thread programming

동일하거나 비슷한 작업을 하는 복수의 태스크 생성 데이터 인코딩 / 디코딩 , 대규모 계산 등에 사용

◦압축 , 이미지 프로세싱 , 행렬 계산 등 적절한 부하 분산 (Load Balancing) 요구

◦데이터를 적절한 단위 (chunk) 로 분할하여 작업 할당

데이터 분해 방식

Data

chunkchunk

chunkchunk

chunkchunk

chunkchunk

decomposi-tion

thread thread

thread thread

loadbalancing

chunkchunk

chunkchunk

chunkchunk

chunkchunk

Page 23: Thread programming

병렬 처리에 루프를 주로 사용하는 경우 데이터 분해 방식 적합

분기 혹은 데이터 부분 가공인 경우 태스크 분해 방식 적합

필요에 따라 복합적으로 사용합니다

대용량 데이터분석 프레임워크맵리듀스 구조

태스크 분해 VS 데이터 분해

block 1

block 2

block 3

block 4

block 5

map 1

map 2

map 3

map 4

map 5

file 1-1

file 1-2

file 2-1

file 2-2

file 3-1

file 3-2file 4-1

file 4-2

file 5-1

file 5-2

reduce 1

reduce 2

output 1

output 2

partitioner로컬 임시파일 / 결과 복사

병합 / 정렬

Page 24: Thread programming

스레드 안전

Page 25: Thread programming

스레드에서 사용해도 안전한 코드의 총칭◦ 스레드에서 사용해도 안전하게 수행되는 코드◦주로 함수에 대해 안전한지 판단합니다

UNIX 계열에서는 스레드 안전에 대해 상세 분류◦재진입 , 병렬 처리 , 시그널 처리에 대한 안전 여부 등◦ 스레드 안전 개념의 혼란 초래

유닉스 표준 (SUSv4) 에서 용어 가지치기 단행◦ 용어와 개념 , 그리고 잔재들을 소개하는 절입니다

“ 스레드 안전” 용어

Page 26: Thread programming

static char buf_sum[16]; 이 함정

스레드 안전하지 않은 코드

지역 변수로 만들어 해결하면 되지만 예제이므로 상황을 복잡하게 만듭니다

Page 27: Thread programming

스레드 안전하지 않은 코드

thread 1

thread 2

sum_strnum()

sum_strnum()

Page 28: Thread programming

뮤텍스를 통한 스레드 안전 시도 실패

스레드 안전하지 않은 코드

Page 29: Thread programming

WHY?

스레드 안전하지 않은 코드

thread 1

thread 2

sum_strnum()

wait… sum_strnum()

T0 T1 T2 T3 T4 T5 T6 T7 T8 T9

unlock&

switching

lock

lockunlock

&switching

sum_strnum()

Page 30: Thread programming

병렬 실행을 보장하도록 작성된 형태를 취햐여 스레드 안전을 획득한 경우

같은 프로세스 내에서 재귀 호출되어도 문제 없음 재진입성이 없는 표준 함수들

◦ strtok, ctime, gethostbyname, rand, srand, …◦ _r 로 끝나는 함수는 안전합니다 (UNIX)

최신 유닉스 표준 (SUSv4) 에서 재진입성은 스레드 안전에 포함되는 개념으로 편입시켜 정의

재진입성

Page 31: Thread programming

재진입은 정적 객체를 사용하는 경우나 극히 제한적인 코드에만 적용 가능

전역 변수의 경우 뮤텍스를 통해 스레드 안전 확보

공유 자원에 락을 걸면 안됩니다◦ 네트워크 , 파일 입출력 등◦ 부분 입출력 후 스위칭되는 경우 기아 유발◦ Isolated I/O 사용 (IOCP 등 )

뮤텍스를 통한 스레드 안전

Page 32: Thread programming

입출력을 담당하는 큐와 스레드

workerthread

workerthread

workerthread

workerthread

뮤텍스로 보호되는 구간

큐 outputthread network

send 등록 send작업 꺼내기

Page 33: Thread programming

함수 실행 중 시그널에 의한 인터럽트 발생 EIN-TER 오류 반환 (Error Interrupt)◦ 멀티스레드 환경에서 lock 함수 호출 도중 시그널 발생 등◦ 세마포어 lock 시도 시그널 안전하지 않음◦ 세마포어 unlock 시도 시그널 안전

스레드 취소 함수 (pthread_cancel) 시 수행중인 함수가 정상 종료하는 경우 비동기 취소 안전

비동기 시그널 안전 , 비동기 취소 안전

Page 34: Thread programming

저수준 입출력 (read, write) 는 버퍼 (PIPE_BUF)보다 입출력이 작은 경우 실행 보장

sig_atomic_t 정수형 변수의 접근 보장 대부분의 함수는 원자적 실행을 보장하지 않습니다

원자적 실행

Page 35: Thread programming

입출력 구간을 나누어 스레드마다 구간 입출력 write() pwrite() 사용 POSIX Realtime Extension 의 AIO 사용

파일 입출력

Page 36: Thread programming

pthread

Page 37: Thread programming

pthread 는 전통적인 API 스펙이므로 한번 익혀두면 오랫동안 별 탈 없이 사용할 수 있습니다

기능별 세분화가 잘 되어있음◦ 복잡합니다

오류 처리 방법◦ 성공시 0 반환 , 실패시 오류 코드 반환◦ errono 전역변수 공유 회피

시그널 처리에 의해 인터럽트되지 않습니다◦ 비동기 시그널 함수는 인터럽트 됩니다

POSIX 스레드

Page 38: Thread programming

Windows Native thread 와 비슷합니다

pthread_create(pthread_t *restrict_thread, const pthread_attr_t *restrict_attr, void *(*s-tart_routine)(void *), void *restrict_arg);

pthread_join/pthread_detach◦ join 은 스레드 대기 , detach 는 스레드 제어 포기

pthread 의 생성 , 종료 , 취소

pthread_create 스레드 생성pthread_exit 스레드 종료pthread_join 스레드를 프로세스에 병합pthread_detach 스레드를 프로세스에서 분리pthread_cancel 스레드 취소

Page 39: Thread programming

pthread_exit 의 반환 주소는 스레드 스택에 두면 안됩니다◦ 스레드 스택이 파괴되면 반환 주소가 날아가면서

pthread_join 으로 대기할 수 없습니다 pthread_cancel 은 강제 종료가 아닙니다

◦ 스레드 중지 요청 (deferred cancel, 지연된 취소 )◦ 스레드는 특정 지점마다 중지 요청이 있는지 확인 후 중지◦ 공유 메모리 , 자원 등 누수 가능성 존재

pthread_mutex_timedlock◦ lock + 타임아웃◦ 타임아웃 시간 이후 오류를 반환합니다

pthread 의 생성 , 종료 , 취소

Page 40: Thread programming

Win32 의 그것과 매우 흡사합니다◦ waitfor- 함수 대신 lock & unlock 함수 사용◦ try, timeout 함수 제공◦객체의 생성 , 뮤텍스 획득 , 해제 , 파괴 함수 제공

뮤텍스 속성◦ pthread_mutexattr_settype(*attr, type)

pthread 뮤텍스

normal errcheck recursive

중복 잠금 교착 상태 오류 반환재귀 잠금 허용(횟수 적용 )

소유권이 없는 잠금 해제 시도 undefined 오류 반환(EPERM)

오류 반환(EPERM)

풀린 뮤텍스에 잠금 해제 시도 undefined 오류 반환(EPERM)

오류 반환(EPERM)

성능 빠름 약간 느림 약간 느림

Page 41: Thread programming

스레드가 특정 조건을 만족할 때까지 대기

조건 변수 (condition variable)

뮤텍스영역

조건변수영역

thread Block mutex

큐 확인

조건 변수 블록(unlock mutex)

item 디큐잉

unlock mutex

item 준비

lock mutex

item 큐잉

unlock mutex

thread B 에시그널 전송

thread A

noyes

signal전송

조건 변수에서 깨어남(lock mutex)

Page 42: Thread programming

조건 변수에 의해 대기하는 경우 뮤텍스 해제◦ 기아 방지

조건 변수에서 깨어나는 경우 즉시 조건 검사를 해야 합니다◦ 복수의 스레드가 깨어나는 경우 경쟁 상태 유발

조건 변수 관련 함수 & 매크로

조건 변수 (condition variable)

pthread_cond_init 조건 변수 초기화pthread_cond_wait 조건 변수 대기pthread_cond_timedwait 대기후 타임아웃 지정

( 지정 시간 후 오류 반환 )

pthread_cond_signal 스레드 하나를 깨움pthread_cond_broadcast 스레드 집합을 깨움pthread_cond_destroy 조건 변수 파괴PTHREAD_COND_INITIALIZER 선언 및 초기화에 사용하는 매크로

Page 43: Thread programming

뮤텍스와 조건 변수는 공유 메모리에 둡니다 프로세스 공유 속성 함수

매개변수◦ pthread_mutexattr_t : 뮤텍스 속성◦ pthread_condattr_t : 조건 변수 속성◦ int pshared : PTHREAD_PROCESS_SHARED,

PTHREAD_PROCESS_PRIVATE

뮤텍스와 조건 변수의 프로세스 공유

pthread_mutexattr_setpshared

뮤텍스 속성에 프로세스 공유 기능 설정

pthread_mutexattr_getpshared

뮤텍스 속성의 프로세스 공유 기능 반환

pthread_condattr_setpshared

조건 변수 속성에 프로세스 공유 기능 설정

pthread_condattr_getpshared

조건 변수 속성의 프로세스 공유 기능 반환

Page 44: Thread programming

스레드들이 특정 코드 지점에 모일 때까지 대기 POSIX.1003.2-2001 에 추가됨

◦ _XOPEN_SOURCE 매크로가 600 이상이면 됩니다 배리어 함수들

배리어

pthread_barrier_init(count)배리어 객체 초기화(count 만큼 wait 호출 전까지 스레드 대기 )

pthread_barrier_destroy 배리어 객체 파괴pthread_barrier_wait 다른 스레드 도착 대기pthread_barrierattr_init 배리어 속성 객체 초기화pthread_barrierattr_destroy 배리어 속성 객체 파괴pthread_barrierattr_getpshared 배리어 공유 속성 확인

pthread_barrierattr_setpsahred 배리어 공유 속성 설정

Page 45: Thread programming

문맥 교환 (context switching) 최소화 바쁜 대기 (busy wait) 를 수행합니다

◦ 대기시간이 길어질수록 효율이 떨어집니다 스핀락 함수들

스핀락

pthread_spin_init 스핀락 객체 초기화pthread_spin_destroy 스핀락 객체 파괴pthread_spin_lock 스핀락 획득pthread_spin_trylock 스핀락 획득 시도 , 실패시 오류 반환 (EBUSY)

pthread_spin_unlock 스핀락 해제

Page 46: Thread programming

공유 자원에 여러 스레드가 효율적으로 접근 reader-lock 이 걸린 경우 writer-lock 은 잠깁니다 위의 함수 원형들과 형태가 비슷합니다

◦ pthread_rwlock_init / destroy◦ rwlock_rdlock / wrlock◦ try__lock, timed__lock

프로세스 공유 속성은 지원하지 않습니다

reader-writer 락

Page 47: Thread programming

Win32 의 그것과 비슷합니다 GCC 에서는 __thread 예약어 제공 스레드 안전한 함수를 작성할 때 유용합니다 int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));◦ 파괴자 함수를 직접 지정할 수 있습니다

기타◦ pthread_key_delete(key)◦ pthread_setspecific(key, value)◦ value pthread_getspecific(key)

TLS

Page 48: Thread programming

Win32 뮤텍스의 WAIT_ABANDONED_0◦뮤텍스가 잠긴 채 스레드가 죽었습니다◦ pthread 에서는 EOWNERDEAD 오류 반환

잠긴 뮤텍스를 자동으로 다른 스레드가 받음◦ pthread_mutexattr_(set/get)robust(attr, int ro-

bust)◦ robust 값

PTHREAD_MUTEX_STALLED : 교착 상태 PTHREAD_MUTEX_ROBUST : 다른 스레드가 뮤텍스를 받음

◦뮤텍스를 받은 스레드는 뮤텍스 인계 함수 호출 pthread_mutex_consistent 처리하지 않는 경우 lock 함수 실패 , ENOTRECOVERABLE

반환

robust 뮤텍스

Page 49: Thread programming

WndProc 이 아닙니다 스레드 종료 / fork() 호출 등 예외 이벤트 처리용

◦ fork() 이전에 비동기 작업을 정지하거나 제거 스레드 클린업 핸들러

◦ 스레드가 종료될 때 실행하는 함수 등록◦ atexit() 함수의 스레드 버전◦핸들러에 등록된 이벤트는 스택처럼 쌓입니다

pthread_cleanup_push(routine, arg) pthread_cleanup_pop(n) : 0 or ~0 ~0 이면 클린업 핸들러 실행 후 제거 , 0 이면 바로 제거

이벤트 핸들러

Page 50: Thread programming

스레드 fork 핸들러◦ pthread_atfork(prepair, parent, child)

prepair 는 fork() 이전에 실행할 함수 parent 는 fork() 호출 이후 부모 프로세스가 실행할 함수 child 는 fork() 호출 이후 자식 프로세스가 실행할 함수

◦ fork() 를 호출하는 스레드만 복제됩니다 다른 스레드는 복제되지 않습니다 복잡해지므로 fork() 를 사용하지 않는 것을 권장합니다

이벤트 핸들러

Page 51: Thread programming

OpenMP 프로그래밍

Page 52: Thread programming

간단한 지시어를 사용하여 멀티스레딩을 적용할 수 있는 기법

이식성이 높고 학습 용이 코드 수정을 최소화하고 멀티스레딩 적용 시간 단축 짧은 역사 (1996~) 다양한 언어 (C/C++, Fortran 등 ) 지원 #define _OPENMP 매크로 정의됨

OpenMP

Page 53: Thread programming

OpenMP 3.0 코드는 실행이 불가능합니다◦ visual studio 2012 에서 OpenMP 3.0 을 지원하지

않습니다◦ Intel Parallel Studio 설치시 지원 가능합니다 ($1,599)◦ 해봤더니 안됩니다 ㅠㅠ

OpenMP 3.0

Page 54: Thread programming

전처리기 지시어 이하 문장 (혹은 블록 ) 을 여러 스레드로 처리합니다

간단 예제

Page 55: Thread programming

기본 OMP 스레드 수 == CPU 수 #pragma omp … num_threads(n)

◦ 한번 적용 omp_set_num_threads(n)

◦ 함수입니다◦ 호출 이후 OpenMP 스레드를 자동으로 n 만큼 만듭니다

동작 스레드 개수

Page 56: Thread programming

워크 쉐어링 구조

loop 모델 루프 작업을 분할하여 병렬 처리

sections 모델직렬 코드 블록들을 몇 개의 구간으로 분할하여 병렬 처리

single 모델병렬 처리되는 구간회서 1 회성 실행 구간 설정

태스크 구조 task 모델(3.0)

태스크 단위 작업을 처리하는 스레드 생성

OpenMP 병렬화 모델

Page 57: Thread programming

OpenMP 병렬 구간에서 변수의 공유 설정

변수의 공유

사설(private)

속성

private(list) list 에 지정된 변수를 TLS 에 생성

firstprivate(list)

list 를 main 스 레 드 의 초기 화값으 로 초기화

lastprivate(list)

마지막 스레드의 해당 변수 값을 main스레드에 반영

공유 속성 shared(list)명시 적 으 로 모든 스 레 드 가 공 유 하 는 변수로 선언

기본 설정 default(value)value : shared, none변수의 공유 설정 기본값 , parallel 병렬 구간이 시작할 때 설정

Page 58: Thread programming

reduction(operand:var)◦ var 값에 operand 연산자 수행◦ 내부에서 묵시적으로 private 지시어 수행

default 가 none 일 경우 명시적으로 shared 로 선언 reduction 연산

변수의 환원

연산자 초기값 연산자 초기값+ 0 & ~0

* 1 | 0

- 0 && 1

^ 0 || 0

max 표현 가능한 최소값 min 표현 가능한 최대값

Page 59: Thread programming

변수의 공유와 환원

Page 60: Thread programming

루프는 반복 횟수가 정해져 있어야 함 #pragma omp parallel 이후 문장부터 스레드

생성 for 문 반복 횟수 == 분할 개수 * 스레드 수 모든 스레드가 완료할 때까지 대기 (묵시적 배리어 ) #pragma 두 줄

◦ 합쳐서 기술 가능◦ #… parallel for

omp_get_thread_num()◦ 스레드 번호 반환

loop 모델

Page 61: Thread programming

loop 모델

main()

#pragma omp parallel

#pragma omp for

/* implicit barrier */

i = 0i = 1i = 2i = 3

i = 4i = 5i = 6i = 7

Page 62: Thread programming

남는 스레드가 밀린 작업을 하도록 작업 할당

스케줄링

schedule(static [, n]) 라운드 로빈 방식으로 순서대로 n 개씩 할당 . n 생략시 1

schedule(dynamic [, n])

한가한 스레드에게 n 개씩 할당 . n 생략시 1

schedule(guided [, n]) dynamic 과 동일하게 한가한 슬데ㅡ에게 할당하지만 chunk 의 크기가 다름 . 할당되는 chunk 크기는 큰 수에서 n 까지 줄여나감 . n 생략시 1

schedule(auto) 임플리먼테이션이 자동으로 판단schedule(runtime) 실행시 ICV 환경 변수에 의해 결정

Page 63: Thread programming

dynamic 스케줄링은 개별 작업의 수행 시간이 균일하지 못할 때 좀 더 빨리 작업을 끝낸 스레드에게 새로운 chunk 할당

static, dynamic 스케줄링은 chunk 크기가 작은 경우 오버헤드 유발

guided 는 chunk 크기를 점점 줄여 나가며 할당◦ 전체적으로 스케줄링 횟수가 줄어듦

runtime 에 쓰이는 OpenMP 내부 제어 변수(OpenMP ICV) 은 환경 설정 변수나 함수 호출로 설정 가능

스케줄링

Page 64: Thread programming

스레드 작업 순서를 정렬하여 결과를 순서대로 작성 직렬화 / 동기화 기법을 직접 구현하는 것보다 성능

향상

순서 정렬

Page 65: Thread programming

코드의 특정 구간을 다수의 섹션으로 분할하여 병렬 처리하는 태스크 방식

sections 모델

main()

#pragma omp parallel

#pragma omp sections

/* implicit barrier */

section#1

section#2

Page 66: Thread programming

임의의 섹션을 임의의 스레드가 수행◦ 수행 순서는 보장되지 않음

변수 공유 및 환원 가능

sections 모델

Page 67: Thread programming

single 블록은 한번만 수행 스레드 중 가장 먼저 진입하는 스레드가 수행

◦ 나머지 스레드들은 single 작업이 끝날 때 까지 대기 주로 초기화 작업 등에 사용

single 모델

main()

#pragma omp parallel

/* implicit barrier */

single

parallel#1

parallel#1

/* implicit barrier */

Page 68: Thread programming

특정 작업 단위로 분할된구간을 스레드에게 할당 임의의 구간을 임의의 스레드가 수행 루프의 개수 , 중첩 여부 , 재귀 여부에 관계없이 활용

가능

task 모델

taskcode

taskcode

taskcode

thread#1

thread#2

thread#n

taskcode

taskcode

taskcode

Page 69: Thread programming

현재 스레드 (주로 메인 스레드 ) 가 작업 코드에 해당하는 구간을 스레드에게 분배

if◦ 스레드가 작업을 분배할 조건을 설정◦특정 시점부터 분배를 시작하게 할 수 있습니다◦ #pragma omp ~ if(exp)

task 모델

Page 70: Thread programming

untied◦ 스레드가 정지 후 재시작 (suspeded resume)될 때 다른

스레드가 작업을 이어 받도록 설정◦ tied 인 경우 재시작 이후에도 같은 스레드가 작업 ( 기본값 )

task 모델

Page 71: Thread programming

배리어 , 크리티컬 섹션 , 원자적 실행 , OpenMP lock 제공

크리티컬 섹션은 Win32 의 그것과 다를 수 있음 OpenMP lock 은 뮤텍스와 유사

동기화 기능

Page 72: Thread programming

parallel, for, sections, single 의 끝에는 묵시적 배리어가 존재

명시적 배리어◦ #pragma omp barrier◦ #pragma omp taskwait

자식 task 대기 묵시적 배리어 제거

◦ #pragma omp ~ nowait◦ loop, sections, single 에 사용

#pragma omp critical [name]◦ name 을 생략하면 이름이 없는 것으로 간주

배리어 , 크리티컬 섹션

조건문과 결합하는 경우

Page 73: Thread programming

#pragma omp atomic [read | write | update | capture]◦ 생략시 update 로 간주

atomic 형식 분류 (x : 대상 )

원자적 실행

read v = x; v 는 TLS 변수write x = exp; exp 는 보호되지 않음

update

x++;x--;++x;--x;x binary_operator = exp;

대상이 변하면서 읽어야 하는 경우 사용

축약 대입 연산 가능

cap-ture

v = x++;v = x--;v = ++x;v = --x;v = x binary_operator = exp;

update + read공유 데이터를 변경하고 값을 읽어야 하는 다수의 작업에 적합

Page 74: Thread programming

커널 객체 (≒뮤텍스 ) 처럼 사용합니다 omp_lock_t

◦범용 lock omp_nest_lock_t

◦ 중첩 가능한 lock lock 관련 함수들

OpenMP lock

omp_init_lock 초기화omp_destroy_lock 객체 파괴omp_set_lcok lock 획득omp_unset_lcok lock 해제omp_test_lcok lock 획득 시도 (0 == 실패 )

Page 75: Thread programming

master 스레드에서 동작하는 코드 지정 묵시적 배리어 , private 변수가 없습니다

master 지시어

main()

#pragma omp parallel

#pragma omp sections

/* implicit barrier */

parallel#1

parallel#2

master

Page 76: Thread programming

행렬 곱 (2x2) 을 어떻게 구하시나요 ?◦ a₁₁ * a₁₁ + a₁₂ * a₂₁ a₁₁ * a₁₂ + a₁₂ * a₂₂◦ a₂₁ * a₁₂ + a₂₂ * a₂₁ a₂₁ * a₁₂ + a₂₂ * a₂₂

중첩된 루프의 경우 가장 바깥 루프를 기준으로 병렬화◦ 코어수 보다 분할횟수가 적은 경우 나머지 스레드는 돌지

않습니다◦ for(i=0; i < 3; ++i) 쿼드코어에서 4번째 스레드 X

중첩된 루프의 병렬화

Page 77: Thread programming

omp_set_nedsted(0 or ~0)◦ TRUE / FALSE 로 중첩 기능을 설정합니다◦ OMP_NESTED 환경 변수로도 설정 가능합니다

중첩된 루프의 병렬화

Page 78: Thread programming

collapse(n)◦ 중첩된 n 단계 루프를 풀어버립니다◦ OpenMP 3.0 이상에서 지원

중첩된 루프의 병렬화

Page 79: Thread programming

#pragma omp threadprivate(list)◦ 리스트에 있는 변수를 TLS 변수로 선언◦ 원본 변수는 정적 변수◦ 예제는 길어서 생략합니다

TLS 변수의 복사◦ 원본은 마스터 스레드◦ TLS 변수 중 일부만 복사 가능◦ #pragma omp parallel copyin(list)

병렬 구간이 시작될 때 TLS 변수 복사◦ #pragma omp single copyprivate(list)

single 코드 블록이 끝나면서 TLS 변수 복사

OpenMP TLS

Page 80: Thread programming

Internal Control Variable 의 약자입니다 스레드 개수 관련 ICV

ICV 와 환경 변수

환경 변수 OMP_NUM_THREADS=n[, …]

생성할 스레드의 개수 설정중첩 단계별로 ‘ ,’ 로 구분

함수

omp_set_num_threads(n) 생성할 스레드의 개수 설정omp_get_num_threads() 현재 스레드 개수 제한omp_get_max_threads() 현재 최대 스레드 개수 제한omp_get_team_size() 중첩 단계

Page 81: Thread programming

중첩 관련 ICV

스케줄링 관련 ICV ( 스케줄링 방법이 runtime 인 경우 )

ICV 와 환경 변수

환경 변수 OMP_NESTED value : true or false

함수

omp_set_nested(value) value : 0 or ~0

omp_get_nested() 0 or ~0 반환omp_get_level 현재 병렬화 중첩 단계 반환

환경 변수 OMP_SCHEDULE=type [, chunk]

type : static, dynamic, guided, auto

함수omp_set_schedule(omp_sched_t kind, int modifier)

kind : 스케줄 방법modifier : chunk 개수

int omp_get_schedule() 현재 스케줄링 방법

Page 82: Thread programming

ICV 와 환경 변수schedule(static [, n]) 라운드 로빈 방식으로 순서대로 n 개씩 할당 . n

생략시 1

schedule(dynamic [, n])

한가한 스레드에게 n 개씩 할당 . n 생략시 1

schedule(guided [, n]) dynamic 과 동일하게 한가한 슬데ㅡ에게 할당하지만 chunk 의 크기가 다름 . 할당되는 chunk 크기는 큰 수에서 n 까지 줄여나감 . n 생략시 1

schedule(auto) 임플리먼테이션이 자동으로 판단schedule(runtime) 실행시 ICV 환경 변수에 의해 결정

Page 83: Thread programming

동적 조정 관련 ICV ( 실행 중 스레드 개수 조정 )

기타

omp_get_wtime() 은 부팅 이후도 반환할 수 있음

ICV 와 환경 변수

환경 변수 OMP_DYNAMIC=value value : true or false

함수omp_set_dynamic(value) value : 0 or ~0

omp_get_dynamic() 동적 조정 여부 반환

함수double omp_get_wtime()

과거 특정 시점부터 흐른 시간(time 처럼 쓰입니다 )

double omp_get_wtick() ↑ 의 정밀도 ( 단위 ) 반환

Page 84: Thread programming

parallel for sections single task

private ● ● ● ● ●

firstprivate ● ● ● ● ●

lastprivate ● ●

shared ● ●

default ● ●

reduction ● ● ● ●

copyin ●

copyprivate ●

schedule ●

ordered ●

nowait ● ● ●

untied ●

if ●

collapse ●

num_threads

OpenMP 지시어 정리

Page 85: Thread programming

성능을 고려한 프로그래밍

Page 86: Thread programming

가짜 공유

[0]

[1]

[2]

[3]

[0]

[1]

[2]

[3]

CPU 0(thread 1)

CPU 1(thread 2)

cacheaccess

cacheaccess

64 byte 64 byteinvalid

invalid

64byte 를 캐시에서 읽어들이면서 생기는 공유 문제◦ 수정할 때마다 상대방 CPU 캐시 무효화 요청

int cnt_sheep[]

Page 87: Thread programming

가짜 공유에 의한 성능 저하

가짜 공유

Page 88: Thread programming

컴파일러 단계에서 BSS 최적화 수행 패딩 비트를 설계합니다

◦컴파일러에게 맡겨버리는 방법 (GCC)

가짜 공유를 피하는 방법

Page 89: Thread programming

전역 변수 , 힙 , 공유 메모리를 지양합니다◦ 전역 변수를 TLS 에 복사하거나 합니다◦ 병렬 구간에서 chunk 크기를 조절합니다

pipeline 모델에서는 chunk 크기를 동적으로 조절하게 합니다

기타 고려 사항