[2B7]시즌2 멀티쓰레드프로그래밍이...

68
시즌 2 : 멀티쓰레드 프로그래밍이 이리 힘드나요? (Lock-free에서 Transactional Memory까지) 정내훈 한국산업기술대학교 게임공학부

Transcript of [2B7]시즌2 멀티쓰레드프로그래밍이...

Page 1: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

시즌 2 : 멀티쓰레드

프로그래밍이 왜 이리

힘드나요? (Lock-free에서 Transactional Memory까지)

정내훈

한국산업기술대학교 게임공학부

Page 2: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

발표자 소개

KAIST 전산과 박사

− 전공 : 멀티프로세서 CPU용 일관성 유지 HW

NCSoft 근무

− Alterlife 프로그램 팀장

− Project M(현 Blade & Soul) 프로그램 팀장

− CTO 직속 게임기술연구팀

현 : 한국산업기술대학교 게임공학과 부교수

− 학부 강의 : 게임서버프로그래밍, 멀티코어프로그래밍

− 대학원 강의 : 멀티코어프로그래밍, 심화 게임서버 프로그래밍

2-2

Page 3: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

참고

삼성 첨단기술연수소와 CJ E&M에서

강의중인 내용 반영

− 40시간 강의 (실습 포함) => 뒷부분 만 발췌

대학원 4주 강의 분량의 압축

2-3

Page 4: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

2-4

목차

도입 : 그래도 멀티쓰레드 프로그램을 하시려구요?

대책

성능

미래 또는 현실

Page 5: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

도입

제가 하는 발표 들어본 적이 있으신 분?

멀티쓰레드 프로그래밍 경험 있으신 분?

Lock-free 자료 구조가 무엇인지 아시는 분?

2-5

DEVIEW 2013

CJ E&M, NDC, KGC, 삼성, JCE

Page 6: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

도입

멀티쓰레드 프로그래밍의 위험성

− “자꾸 죽는데 이유를 모르겠어요”

자매품 : “이상한 값이 나오는데 이유를 모르겠어요”

− “더 느려져요”

2-6

[미] MuliThreadProgramming [mʌ́ltiθred-|proʊgrӕmɪŋ] : 1. 흑마술, 마공

2. 위력이 강대하나 다루기 어려워 잘 쓰이지 않는 기술

Page 7: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

도입

멀티쓰레드 프로그래밍의 어려움 (한페이지 요약)

− Data Race : 2를 5천만번 더했는데 1억이 안 나오는 경우.

Lock을 사용해 해결

− 성능 : 싱글 쓰레드 버전보다 더 느림

Lock 쓰지 말자

− 컴파일러 : 변수를 참조했는데, 컴파일러가 무시

volatile 키워드로 해결, atomic으로 해결

− CPU : 프로그램 실행순서를 자기 마음대로 변조

asm mfence; 명령으로 해결, atomic으로 해결

− Cache : -1을 썼는데 65535가 써짐

포인터 주소 확인하기.

2-7

ABA

문제는?

Page 8: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

목차

도입

대책 : Lock-free 프로그래밍이 도대체

뭐야?

성능

미래 또는 현실

2-8

Page 9: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

현실의 멀티쓰레드 프로그램은?

− 멀티 코어에서 여러 쓰레드가 동시에 실행된다.

− 쓰레드간의 데이터 공유 및 동기화는 안전한

Lock-free 자료구조를 통해서 이루어진다.

− 언리얼 3 : 디스플레이 리스트 Queue

− 각종 게임 서버 : Job Queue외 다수

2-9

Page 10: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

성능을 위해서는 Lock-free 알고리즘을

사용하여야 한다.

사용하지 않으면

− 병렬성 감소

− Priority Inversion

− Convoying

− /* 성능이 떨어지고 랙이 발생한다 */

− /* 작년에 보여드렸어요~~ */

2-10

Page 11: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Lock-free 알고리즘이란?

− 여러 개의 쓰레드에서 동시에 호출했을 때에도

정해진 단위 시간마다 적어도 한 개의 호출이

완료되는 알고리즘.

??????

Page 12: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Lock-free 알고리즘이란?

− 자료구조 및 그것에 대한 접근 방법

예) QUEUE : enqueue, dequeue

예) STACK : push, pop

예) 이진 트리 : insert, delete, search

2-12

Page 13: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Lock-free 알고리즘이란?

− 멀티쓰레드에서 동시에 호출해도 정확한 결과를

만들어 주는 알고리즘

STL 탈락.

− Non-Blocking 알고리즘

다른 쓰레드가 어떤 상태에 있건 상관없이 호출이 완료된다.

Lock 사용 시 탈락.

− 다른 쓰레드와 충돌하였을 경우에도 언제나 적어도

하나의 승자가 있어서, 승자는 delay없이 완료됨.

Page 14: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

(보너스)

− Wait-free 알고리즘은?

호출이 다른 쓰레드와 충돌해도 모두 delay없이 완료 된다.

추가 상식

− LOCK을 사용하지 않는다고 lock-free 알고리즘이

아니다!!!

− LOCK을 사용하면 무조건 lock-free알고리즘이 아니다.

Page 15: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

알고리즘의 분류

2-15

알고리즘

싱글쓰레드

멀티쓰레드

Blocking

Non-blocking

Lock-free

Wait-free

….

….

Page 16: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

예) Blocking 알고리즘

2-16

mylock.lock();

sum = sum + 2;

mylock.unlock();

BLK_QUEUE::push(int x) {

Node *e = New_Node(x);

qlock.lock();

tail->next = e;

tail = e;

glock.unlock();

} while (dataReady == false);

_asm mfence;

temp = g_data;

Page 17: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

왜 Blocking인가?

− dataReady에 true가 들어가지 않으면 이 알고리즘은

무한 대기, 즉 다른 쓰레드에서 무언가 해주기를

기다린다.

− 여러 가지 이유로 dataReady에 true가 들어오는 것이

지연될 수 있다.

Schedule out, 다른 쓰레드 때문에 대기

2-17

while (dataReady == false);

_asm mfence;

temp = g_data;

Page 18: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Non-blocking은?

2-18

_asm lock add sum, 2;

LF_QUEUE::push(int x) {

Node *e = new_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL == next) {

if (CAS(&(last->next), NULL, e)) {

CAS(&tail, last, e);

return;

}

} else CAS(&tail, last, next);

} }

BLK_QUEUE::push(int x) {

Node *e = New_Node(x);

qlock.lock();

tail->next = e;

tail = e;

glock.unlock();

}

Page 19: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Non-blocking은?

2-19

if (dataReady == false) return false;

_asm mfence;

temp = g_data;

while (dataReady == false);

_asm mfence;

temp = g_data;

Page 20: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

CAS가 보이는데???

− Lock-free 알고리즘의 핵심

− CAS가 없이는 대부분의 non-blocking

알고리즘들을 구현할 수 없다.

Queue, Stack, List…

− CAS를 사용하면 모든 싱글쓰레드 알고리즘

들을 Lock-free 알고리즘으로 변환할 수 있다!!!

2-20

Page 21: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

CAS − CAS(&A, old, new);

− 의미 : A의 값이 old면 new로 바꾸고 true를 리턴,

아니면 false를 리턴.

− 다른 버전의 의미 : A메모리를 다른 쓰레드가 먼저

업데이트 해서 false가 나왔다. 모든 것을 포기하라.

2-21

Page 22: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Lock-free 알고리즘은 어떻게 구현되는가?

알고리즘의 동작이란?

− 기존의 자료구조의 구성을 다른 구성으로 변경하거나

자료구조에서 정보를 얻어내는 행위

2-22

3 Head Tail 1 9 X

3 Head

Tail

1 9 X

push(35);

35

Page 23: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Lock-free 알고리즘은 어떻게 구현되는가?

2-23

자료구조를 변경을 시도한다.

성공했는가? 완료

no

yes

(time machine) 시도 전으로 되돌아간다..

???

???

Page 24: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Lock-free 알고리즘은 어떻게 구현되는가?

앞의 알고리즘이 불가능 하므로

2-24

자료구조의 변경을 시도한다.

but, 다른 쓰레드가 먼저 변경했으면 시도 취소.

성공했는가? 완료

yes

no

현재의 자료구조를 파악한다.

Page 25: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책 2-25

자료구조의 변경을 시도한다.

but, 다른 쓰레드가 먼저 변경했으면 시도 취소. CAS

while (true) {

int old_sum = sum;

if (true == CAS(&sum, old_sum, old_sum+2)) break;

}

mylock.lock();

sum = sum + 2;

mylock.unlock();

while (CAS(&lock, 0, 1));

sum = sum + 2;

lock = 0;

차이점?

Page 26: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책 2-26

자료구조의 변경을 시도한다.

but, 다른 쓰레드가 먼저 변경했으면 시도 취소. CAS LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL != next) continue;

if (CAS(&(last->next), NULL, e,

&tail, last, e))

return;

}

}

BLK_QUEUE::push(int x) {

Node *e = New_Node(x);

qlock.lock();

tail->next = e;

tail = e;

glock.unlock();

}

Page 27: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책 2-27

현실

LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL != next) continue;

if (CAS(&(last->next), NULL, e,

&tail, last, e)) return;

}

}

LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL == next) {

if (CAS(&(last->next), NULL, e)) {

CAS(&tail, last, e);

return;

}

} else CAS(&tail, last, next);

} }

하지만 2개의 변수에 동시에

CAS를 적용할 수 는 없다!

Page 28: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

− 알고리즘이 많이 복잡하다.

− 그래서 작성시 실수하기가 쉽다.

− 실수를 적발하기가 어렵다.

하루에 한두 번 서버 크래시

가끔 가다가 아이템 증발

− 제대로 동작하는 것이 증명된 알고리즘을

사용해야 한다.

2-28

Page 29: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

Lock-Free 알고리즘의 장단점

− 장점

성능!!

높은 부하에도 안정적!!

− 단점

생산성 : 복잡한 알고리즘

신뢰성 : 정확성을 증명하는 것은 어렵다.

확장성 : 새로운 메소드를 추가하는 것이 매우 어렵다.

메모리 : 메모리 재사용의 관리가 어렵다. ABA문제

2-29

Page 30: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

대책

결론

− 믿을 수 있는 non-blocking container들을

사용하라.

Intel TBB, Visual Studio PPL

− 자신을 포함한 출처가 의심스러운 알고리즘은

정확성을 증명하고 사용하라.

정확성이 증명된 논문에 있는 알고리즘은 OK.

2-30

Page 31: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

목차

도입

대책

성능 : 데스크탑에서 동접 1만 서버를

만들어 보자.

미래 또는 현실

2-31

Page 32: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

성능

간단한 MMORPG 서버를 만들어 보자

− 1000x1000 world

− 60x60 sector

− 시야 30

− 1000마리의 몬스터

플레이어 접근 시 자동 이동 및 공격

− 이동/공격/채팅 가능

− Windows에서 IOCP로 구현

− <2013년 게임서버프로그래밍 텀프로젝트 by 임상수>

Page 33: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

성능

성능 향상을 위해

− 시야 처리시 검색 성능을 위해 월드를 sector로 분할하여 주위

sector만 검색

병렬 검색을 위해 tbb::concurrent_hash_map 사용

− 몬스터 AI 처리를 모든 쓰레드에서 균등하게 나누어 처리하기

위해 timer와 event시스템 사용

timer queue 병렬 등록을 위해 tbb::concurrent_priority_queue를 사용

− 객체 id의 재사용을 막고 메모리 재사용을 위해 객체 id와 객체

배열의 인덱스를 쌍으로 관리

<id, index>의 병렬 검색을 위해 tbb::concurrent_hash_map 사용

Page 34: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

성능

성능 측정용 DummyClient

− 서버에게 부하를 걸 수 있도록 여러 명의 Player를 에뮬레이션

해주는 프로그램

− 사람이 플레이 하는 것과 비슷하게 Player를 주기적으로 랜덤한

방향으로 이동 시킴

− 한명의 유저당 하나의 소켓 연결을 하므로 서버에서는 일반

클라이언트 접속과 DummyClient접속이 서로 차이가 없음

− 훌륭한 UNIT TESTER

− /* 이것도 IOCP로 구현, Direct3D로 유저 분포 실시간 디스플레이

*/

Page 35: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

성능 2-35

Page 36: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

성능

실행 결과

− Intel i7 920 2.67GHz 머신에서 실행

동접 8000 정도까지

− Lock-free 자료구조로 최적화 하기 전에는 동접

3000 정도가 한계.

Page 37: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

목차

도입

대책

성능

미래 또는 현실 : Transactionl 메모리,

그리고…

2-37

Page 38: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

C++11

− 멀티쓰레드 프로그래밍 API의 표준화

− 멀티 쓰레드 표준 라이브러리

<thread>, <mutex>, <atomic>, <memory>

− Boost ASIO

− G++ 4.8, Visual Studio 2013

아직 최적화에 문제 : shared_ptr, mutex

Page 39: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

멀티쓰레드 프로그래밍 도우미 (1/2)

− Intel TBB

좋은 성능, 유용한 라이브러리

Concurrent 자료 구조

− Visual Studio PPL

Intel TBB와 유사

− OpenMP

컴파일러 레벨에서 병렬 프로그램 API를 제공

성능과 골치 아픈 문제들은 그대로

− 그 외, CK(Concurrency Kit), Noble Library

Page 40: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

암담한 현실

− Blocking Algorithm

성능 저하, priority inversion, convoying

Deadlock!

− Non-blocking Algorithm

높은 제작/검증 난이도

어쩌라고????

Transactional Memory!

Page 41: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

Transactional Memory?

2-41

Tim Sweeny at GameTech2010

Page 42: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

Transactional Memory는

− 아까 본 바로 그 그림을 그대로 구현한 것…

자료구조를 변경한다.

다른 쓰레드와

충돌했는가? 완료 no

yes

(time machine) 시도 전으로 되돌아간다.. ???

Page 43: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

Transactional Memory가 좋은 이유?

− 생산성!

− 싱글쓰레드 프로그램을 그대로 쓸 수 있음

하지만 멀티쓰레드에서 Lock-free하게 실행됨.

LF_QUEUE::push(int x) {

Node *e = new Node(x);

atomic {

tail->next = e;

tail = e;

}

}

LF_QUEUE::push(int x) {

Node *e = New_Node(x);

while (true) {

Node *last = tail;

Node *next = last->next;

if (last != tail) continue;

if (NULL == next) {

if (CAS(&(last->next), NULL, e)) {

CAS(&tail, last, e);

return;

}

} else CAS(&tail, last, next);

} }

Transactionl Memory 구현

Page 44: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

Transactional Memory의 사용법

− 기존의 싱글쓰레드 프로그램에서 다른

쓰레드와 충돌이 일어날 수 있는 구간을

transaction으로 선언한다.

끝, 이게 전부

Page 45: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

어떻게 가능한가???

1. transaction 구간에서 읽고 쓴 메모리를 다른

쓰레드에서 접근했는지 검사한다.

write는 실제 메모리에 쓰지 않고 잠시 대기

2. 다른쓰레드에서의 접근이 있었으면 모든

업데이트를 무효화 한 후 다시 시도.

3. 다른쓰레드에서의 접근이 없었다면 transaction

구간에서의 update를 실제 메모리에 update

Page 46: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

그게 구현 가능하다고??

어떻게

− Software적으로 모든 공유 메모리 업데이트를

관리하면 됨 (STM: Software Transactional Memory)

− 또는, 하드웨어가 알아서 해줌 (HTM: Hardware

Transactional Memory)

2-46

Page 47: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

Transactional Memory

장점

− 높은 생산성

기존의 프로그램을 그대로 사용 가능

단점

− STM : 오버헤드로 인한 속도 저하

− HTM : HW 필요.

한계점

− 쓰레드가 너무 많을 경우 잦은 충돌로 인한 성능향상의 한계

2-47

Page 48: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

Transactional Memory

지금까지는 꿈속의 개념이고 8 Core이상에서

STM의 성능을 기대하는 정도였으나.

Intel에서 일을 저지름.

2-48

2013년 6월 HASWELL말매

Haswell은 HTM을 지원.

Page 49: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

현실

Intel Haswell

2-49

Page 50: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

트랜잭션 메모리의 구현

Haswell의 HTM

− 복수개의 메모리에 대한 Transaction을

허용한다.

L1 Data Cache의 크기 만큼

− CPU에서 transaction 실패시의 복구를

제공한다.

메모리와 레지스터의 변경을 모두 Roll-back한다.

− Visual Studio 2012, update 2에서 지원

2-50

Page 51: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

트랜잭션 메모리의 구현

하드웨어 트랜잭션 메모리 예제

2-51

DWORD WINAPI ThreadFunc(LPVOID lpVoid)

{

for (int i=0;i<500000000 / num_thread;++i) {

while (_xbegin() != _XBEGIN_STARTED) _xabort(0);

sum += 2;

_xend();

}

return 0;

}

Page 52: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

트랜잭션 메모리의 구현

하드웨어 트랜잭션 메모리 예제 (Set의 Add)

2-52

bool Add(int key)

{

NODE *pred, *curr;

NODE *node = new NODE(key);

while(true) {

pred = &head; curr = pred->next;

while (curr->key < key) {

pred = curr; curr = curr->next;

}

if (_XBEGIN_STARTED != my_xbegin()) { _xabort(0); continue; }

if (!validate(pred, curr)) {

_xabort(0);

continue;

}

if (key == curr->key) {

_xend();

delete node;

return false;

} else {

node->next = curr; pred->next = node;

_xend();

return true;

} } }

HTM

Lock-Free

Page 53: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

트랜잭션 메모리의 구현

Haswell HTM의 한계

− 모든 알고리즘에 적용 불가능

HW 용량 한계 => 알고리즘의 맞춤형 수정 필요.

Nested Transaction 불가능 => 가능은 한데…

− 오버헤드

모든 레지스터 내용 저장 및 Roll-back

2-53

그리고…

Page 54: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

트랜잭션 메모리의 구현 2-54

Page 55: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

트랜잭션 메모리의 구현 2-55

T_T

Page 56: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

미래

HTM이 업그레이드 되어서 보급되면

끝인가?

− 쓰레드가 많아 질 수록 충돌확률이 올라가

TM의 성능이 떨어진다.

− 64Core 정도가 한계일 것이라고 예측하고

있다. (2010 GameTech, Tim Sweeny)

Page 57: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

미래

왜 쓰레드사이에 충돌이 생기는가?

− C 스타일 언어를 사용하기 때문이다.

공유 메모리

side effect

해결책은? C 비슷한 언어를 버린다.

− 대신 함수형, 논리형 언어 사용

공유 메모리 없고 side effect없음

Page 58: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

새로운 언어

주목받고 있는 언어

− 하스켈

순수 함수형 언어로 1990년에 개발

개념은 뛰어나나 난이도로 인해

많이 사용되지 못하고 있음.

− Earlang

에릭슨에서 전자 계산기용으로

1982년에 개발

Scalable한 서버 시스템에 자주

사용되고 있음

− Go, RUST

2010GDC

Page 59: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

새로운 언어

함수형 언어의 프로그래밍 스타일

− 모든 변수가 불변이다. (C++의 const)

불변 : 한번 값이 정해지면 바뀌지 않음.

− 불변 변수(immutable variable)는 data race를 일으키지

않는다.

Page 60: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

새로운 언어

하스켈

− 순수 함수형 언어로 1990년에 개발

− 개념은 뛰어나나 난이도로 인해 많이 사용되지 못하고 있음.

− 병렬성

순수 병렬성 (Parallelism) : 언어에 내재된 병렬성 이용, 항상 같은

결과값, data race나 deadlock이 전혀 없음, I/O처리 안됨

동시 실행성 (Concurrency) : I/O 처리를 위해 사용. I/O 실행순서

제어는 프로그래머가 해줘야 함. data race나 deadlock이 가능.

− 성능 문제 : Lazy Evaluation, Fine Grained Execution

Page 61: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

새로운 언어

Erlang

− 에릭슨에서 전자 교환기용으로 1982년에 개발

− Scalable한 서버 시스템에 자주 사용되고 있음

2010GDC

Page 62: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

새로운 언어

Erlang(사용예) (from wikipedia)

− Amazon.com : to implement SimpleDB, providing database

services as a part of the Amazon Web Services offering

− Facebook : to power the backend of its chat service, handling

more than 100 million active users

− WhatsApp : to run messaging servers, achieving up to 2 million

connected users per server.

− GitHub : used for RPC proxies to ruby processes

− CouchDB, Couchbase Server, Mnesia, Riak,

SimpleDB

Page 63: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

새로운 언어

Erlang

− process 단위의 병렬성

OS process(x), thread(x), SW context(o)

수천만개의 process 동시 실행 가능

− 2006년부터 SMP(multicore) 지원

− process사이의 동기화

shared-nothing asynchronous message passing으로 구현.

queue통한 message passing으로 동기화

– 분산 처리 가능

Page 64: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

새로운 언어

Erlang : 분산 I/O 구현 예 % Create a process and invoke the function

% web:start_server(Port, MaxConnections)

ServerProcess = spawn(web, start_server, [Port, MaxConnections]),

% Create a remote process and invoke the function

% web:start_server(Port, MaxConnections) on machine RemoteNode

RemoteProcess = spawn(RemoteNode, web, start_server, [Port, MaxConnections]),

% Send a message to ServerProcess (asynchronously).

% The message consists of a tuple with the atom "pause" and the number "10".

ServerProcess ! {pause, 10},

% Receive messages sent to this process

receive

a_message -> do_something;

{data, DataContent} -> handle(DataContent);

{hello, Text} -> io:format("Got hello message: ~s", [Text]);

{goodbye, Text} -> io:format("Got goodbye message: ~s", [Text])

end.

http://en.wikipedia.org/wiki/Erlang_(programming_language)

Page 65: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

새로운 언어

함수형 언어의 문제

− 생산성

전혀 다른 스타일의 프로그래밍

− 성능

구조체를 함수에 전달할 때 포인터가 아니라 내용을 전달하는 것과

비슷한 오버헤드.

너무 자잘한 병렬화

− IO 문제

IO는 순서대로 행해져야 하는데 함수형 언어에서 IO Operation의

순서를 정해주는 것이 비효율적이다.

Page 66: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

정리

Lock-free 알고리즘이 무엇인가?

− 어떤 조건을 만족해야 하는가?

− 어떻게 구현되는가?

− 왜 어려운가?

− 하지만 왜 써야만 하는가.

멀티쓰레드 프로그래밍의 미래.

− Transactional Memory

진격의 INTEL

− 새로운 언어의 필요

2-66

Page 67: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

NEXT

다음 주제(내년???)

− Lock-free search : SKIP-LIST

− ABA Problem, aka 효율적인 reference

counting

− 고성능 MMO서버를 위한 non-blocking

자료구조의 활용

2-67

Page 68: [2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요

Q&A

연락처

[email protected]

− 발표자료 : ftp://210.93.61.41 id:ndc21 passwd: <바람의나라>

− Slideshare에 올릴 예정.

참고자료

− Herlihy, Shavit, “The Art of Multiprocesor Programming,

Revised”, Morgan Kaufman, 2012

2-68