멀코 5장 non-blocking 알고리즘 - list
問題一覧
1
최댓값을 모르기 때문이다.
2
구현이 쉽다.
3
성능이 안좋다.
4
O
5
왼쪽 glock.lock(); glock.unlock(); glock.unlock(); 오른쪽 glock.lock(); glock.unlock(); glock.unlock(); 아래 glock.lock(); glock.unlock(); glock.unlock();
6
성긴동기화
7
read only 메모리이기 때문에 data race가 아니기 때문이다.
8
O
9
내부적으로 locking이 구현되어 있기 때문이다.
10
list 전체를 잠그는 것이 아니라 노드 개별로 잠궈서 병렬성을 높이기 위해 나온 것이다.
11
next field
12
Add()와 Remove() 시점의 pred, curr가 가리키는 노드는 locking이 되어 있어야 한다., head부터 node 이동을 할 때, lock을 잠그면서 이동해야 한다.
13
스레드 개수가 늘어날수록 빨라진다. 물론 어느정도 되면 다시 느려진다.
14
head
15
coarse-grained synchronization
16
fine-grained synchronization
17
optimistic synchronization
18
lazy synchronization
19
nonblocking synchronization
20
Add() 1: prev->lock(); 2: curr->lock(); 3: prev->unlock(); 4: curr->lock(); 5: prev->unlock(); 6: curr->unlock(); 7: prev->unlock(); 8: curr->unlock(); Remove() 1: prev->lock(); 2: curr->lock(); 3: prev->unlock(); 4: curr->lock(); 5: prev->unlock(); 6: curr->unlock(); 7: prev->unlock(); 8: curr->unlock(); Contains() 1: prev->lock(); 2: curr->lock(); 3: prev->unlock(); 4: curr->lock(); 5: prev->unlock(); 6: curr->unlock(); 7: prev->unlock(); 8: curr->unlock();
21
성긴 동기화
22
lock(), unlock()이 너무 빈번하다.
23
길어지는
24
잠금의 획득과 해제가 너무 빈번하여 이동 시 잠금을 하지 않지 않기 위해 나온 아이디어이다.
25
delete
26
validation 조건
27
재사용되면서 어떻게 사용될지 모르기 때문이다.
28
잠겨진 pred와 curr가 제거되지 않았는지, pred와 curr 사이에 다른 노드가 끼어들지 않았는지
29
pred->next == curr 이면 다른 노드가 끼어들지 않은 것이다.
30
전혀 없다. locking을 하고 검사를 하기 때문이다.
31
다시 처음부터 검색해서 원래 pred, curr로 다시 올 수 있는지 확인한다.
32
다른 스레드들이 pred와 curr를 계속 수정하는 경우 계속 재시도를 하면서 기아를 겪을 수 있다.
33
X
34
add() validate(pred, curr) false true contains() validate(pred, curr) curr.key == key
35
X
36
링크드 리스트에서 성능에 가장 영향에 미치는 것은 검색이다. 성긴 동기화는 한 번 검색하는데, 낙천적 동기화는 처음에 한 번 검색하고 validate 하면서 한 번 더 검색하기 때문에 2배 느리다.
37
스레드 개수가 늘어남에 따라 싱글 스레드보다 성능이 더 좋아지는 것을 확인했다.
38
낙천적 동기화는 코어의 개수가 많지 않으면 성능이 좋지 않다. 이것을 개선하고자 나온 것이 게으른 동기화이다.
39
validate()가 노드를 처음부터 다시 순회하지 않고 수행
40
O
41
read-only
42
marked 필드
43
먼저
44
node == pred pred.next == curr node = node.next
45
validate() !pred.marked && !curr.marked && pred.next == curr contains() curr = curr.next; curr.key == key && !curr.marked
46
curr.marked = true;
47
marking 되어 있지 않은 모든 node는 실제 리스트에 존재하는 살아있는 node이다. 해당 명제가 참이기 때문이다.
48
X
49
빨라진다. 스레드가 하나일 때는 더 느리지만 이 세상에 싱글코어 컴퓨터는 없다.
50
블럭킹 알고리즘이다., 메모리 릭이 있다.
51
재사용하면서 오동작할 수 있기 때문이다.
52
아무도 remove된 node를 가리키지 않을 때, remove 시점에서 중복실행 중인 모든 method의 호출이 종료되었을 때
53
O
54
shared_ptr을 생성할 때 reference counter도 같이 할당해서 이 카운터를 증가하고 감소하면서 0이 되면 해제한다.
55
shared_ptr 객체를 load, store하는 것이 atomic이 아니기 때문이다.
56
싱글 스레드에서만 잘 돌아가고 멀티스레드에서는 죽는다.
57
성긴 동기화, 게으른 동기화, 낙천전 동기화, 세밀한 동기화
58
게으른 동기화, 낙천적 동기화, 성긴동기화, 세밀한 동기화
59
잘 돌아간다. 하지만 성능이 엉망진창이다.
60
X
61
내부적으로 mutex를 사용하고 또한 하나의 lock으로 실행되기 때문이다.
62
local atomic shared ptr
63
X
64
X
65
X
66
if (dataReady == false) return false; temp = g_data;
67
O
68
while (true) { int old_sum = sum; int new_sum = old_sum + 2; if (true == CAS(&sum, old_sum, new_sum)) break; }
69
CAS는 2개의 변수에 동시에 적용할 수 없기 때문이다. 따라서 CAS를 두 번 나누어서 해주어야 하는데 그러면 그 사이에 다른 스레드가 끼어들 수 있다. 난이도가 상승한다.
70
critical section이 충분히 짧아졌기 때문이다.
71
pred의 marking을 확인하면서 변경해야 한다.
72
pred와 next가 curr인 것을 확인하면서 변경해야 한다.
73
한 장소에 주소와 marking을 동시에 저장했다.
74
우리의 컴퓨터가 64비트라고 그 비트를 다 사용하지 않는다. 이 중에 안 쓰는 비트를 사용하여 가능하게 한다.
75
마킹이되었지만 remove되지 않은 노드가 있는 있을 수 있다.
76
pred.next.comparedAndSet(curr, succ, false, false);
77
add() find(head, key) new AtomicMarkableReference(curr, false) pred.next.compareAndSet(curr, node, false, false) remove() find(head, key) curr.next.attemptMark(succ, true) pred.next.compareAndSet(curr, succ, false, false)
78
그냥 포인터가 아닌 합성 포인터를 사용했기 때문이다.
79
성능이 좋다. 메모리 재사용에 stopation이 생길 수 있다.
80
EPOCH
81
Epoch를 사용하기에 정밀하지 않다. 읽고 쓰는 오버헤드가 크다. 데이터 사이즈가 크다.
82
검색 대상이 아니다. 일종의 최적화 시켜주는 코드
83
어느 한 스레드의 thread epoch counter가 증가하지 않을 경우 메모리 릭이나 다름 없다.
84
O
멀코 2장 멀티스레드 프로그래밍
멀코 2장 멀티스레드 프로그래밍
ユーザ名非公開 · 9問 · 2年前멀코 2장 멀티스레드 프로그래밍
멀코 2장 멀티스레드 프로그래밍
9問 • 2年前멀코 3장 메모리 일관성
멀코 3장 메모리 일관성
ユーザ名非公開 · 41問 · 2年前멀코 3장 메모리 일관성
멀코 3장 메모리 일관성
41問 • 2年前고법 2장 헌법
고법 2장 헌법
ユーザ名非公開 · 41問 · 2年前고법 2장 헌법
고법 2장 헌법
41問 • 2年前고법 3장 근로계약
고법 3장 근로계약
ユーザ名非公開 · 42問 · 2年前고법 3장 근로계약
고법 3장 근로계약
42問 • 2年前고법 4장 노동법의 역사
고법 4장 노동법의 역사
ユーザ名非公開 · 23問 · 2年前고법 4장 노동법의 역사
고법 4장 노동법의 역사
23問 • 2年前고법 6장 직장내 성희롱
고법 6장 직장내 성희롱
ユーザ名非公開 · 31問 · 2年前고법 6장 직장내 성희롱
고법 6장 직장내 성희롱
31問 • 2年前멀코 4장 동기화 연산과 cas
멀코 4장 동기화 연산과 cas
ユーザ名非公開 · 20問 · 2年前멀코 4장 동기화 연산과 cas
멀코 4장 동기화 연산과 cas
20問 • 2年前멀코 5-2장 배경이론
멀코 5-2장 배경이론
ユーザ名非公開 · 61問 · 2年前멀코 5-2장 배경이론
멀코 5-2장 배경이론
61問 • 2年前1. 컴퓨터 그래픽스 개요 (컴그)
1. 컴퓨터 그래픽스 개요 (컴그)
ユーザ名非公開 · 34問 · 2年前1. 컴퓨터 그래픽스 개요 (컴그)
1. 컴퓨터 그래픽스 개요 (컴그)
34問 • 2年前2. 2차원 그래픽스의 기본 요소 (컴그)
2. 2차원 그래픽스의 기본 요소 (컴그)
ユーザ名非公開 · 30問 · 2年前2. 2차원 그래픽스의 기본 요소 (컴그)
2. 2차원 그래픽스의 기본 요소 (컴그)
30問 • 2年前3. 2차원 그래픽스 변환 (컴그)
3. 2차원 그래픽스 변환 (컴그)
ユーザ名非公開 · 15問 · 2年前3. 2차원 그래픽스 변환 (컴그)
3. 2차원 그래픽스 변환 (컴그)
15問 • 2年前4. 2차원 그래픽스의 윈도우와 뷰포트 (컴그)
4. 2차원 그래픽스의 윈도우와 뷰포트 (컴그)
ユーザ名非公開 · 16問 · 2年前4. 2차원 그래픽스의 윈도우와 뷰포트 (컴그)
4. 2차원 그래픽스의 윈도우와 뷰포트 (컴그)
16問 • 2年前멀코 6장. 병렬 알고리즘 - QUEUE
멀코 6장. 병렬 알고리즘 - QUEUE
ユーザ名非公開 · 26問 · 2年前멀코 6장. 병렬 알고리즘 - QUEUE
멀코 6장. 병렬 알고리즘 - QUEUE
26問 • 2年前멀코 7장. STACK, SKIP-LIST
멀코 7장. STACK, SKIP-LIST
ユーザ名非公開 · 42問 · 2年前멀코 7장. STACK, SKIP-LIST
멀코 7장. STACK, SKIP-LIST
42問 • 2年前멀코 8장. 병렬 라이브러리
멀코 8장. 병렬 라이브러리
ユーザ名非公開 · 77問 · 2年前멀코 8장. 병렬 라이브러리
멀코 8장. 병렬 라이브러리
77問 • 2年前고법 7장. 임금의 이해
고법 7장. 임금의 이해
ユーザ名非公開 · 46問 · 2年前고법 7장. 임금의 이해
고법 7장. 임금의 이해
46問 • 2年前고법 9장. 인사명령과 징계
고법 9장. 인사명령과 징계
ユーザ名非公開 · 21問 · 2年前고법 9장. 인사명령과 징계
고법 9장. 인사명령과 징계
21問 • 2年前고법 11장. 노동조합법
고법 11장. 노동조합법
ユーザ名非公開 · 45問 · 2年前고법 11장. 노동조합법
고법 11장. 노동조합법
45問 • 2年前컴그 7장. 3차원 객체의 모델링
컴그 7장. 3차원 객체의 모델링
ユーザ名非公開 · 13問 · 2年前컴그 7장. 3차원 객체의 모델링
컴그 7장. 3차원 객체의 모델링
13問 • 2年前컴그 8장. 은면의 제거
컴그 8장. 은면의 제거
ユーザ名非公開 · 9問 · 2年前컴그 8장. 은면의 제거
컴그 8장. 은면의 제거
9問 • 2年前問題一覧
1
최댓값을 모르기 때문이다.
2
구현이 쉽다.
3
성능이 안좋다.
4
O
5
왼쪽 glock.lock(); glock.unlock(); glock.unlock(); 오른쪽 glock.lock(); glock.unlock(); glock.unlock(); 아래 glock.lock(); glock.unlock(); glock.unlock();
6
성긴동기화
7
read only 메모리이기 때문에 data race가 아니기 때문이다.
8
O
9
내부적으로 locking이 구현되어 있기 때문이다.
10
list 전체를 잠그는 것이 아니라 노드 개별로 잠궈서 병렬성을 높이기 위해 나온 것이다.
11
next field
12
Add()와 Remove() 시점의 pred, curr가 가리키는 노드는 locking이 되어 있어야 한다., head부터 node 이동을 할 때, lock을 잠그면서 이동해야 한다.
13
스레드 개수가 늘어날수록 빨라진다. 물론 어느정도 되면 다시 느려진다.
14
head
15
coarse-grained synchronization
16
fine-grained synchronization
17
optimistic synchronization
18
lazy synchronization
19
nonblocking synchronization
20
Add() 1: prev->lock(); 2: curr->lock(); 3: prev->unlock(); 4: curr->lock(); 5: prev->unlock(); 6: curr->unlock(); 7: prev->unlock(); 8: curr->unlock(); Remove() 1: prev->lock(); 2: curr->lock(); 3: prev->unlock(); 4: curr->lock(); 5: prev->unlock(); 6: curr->unlock(); 7: prev->unlock(); 8: curr->unlock(); Contains() 1: prev->lock(); 2: curr->lock(); 3: prev->unlock(); 4: curr->lock(); 5: prev->unlock(); 6: curr->unlock(); 7: prev->unlock(); 8: curr->unlock();
21
성긴 동기화
22
lock(), unlock()이 너무 빈번하다.
23
길어지는
24
잠금의 획득과 해제가 너무 빈번하여 이동 시 잠금을 하지 않지 않기 위해 나온 아이디어이다.
25
delete
26
validation 조건
27
재사용되면서 어떻게 사용될지 모르기 때문이다.
28
잠겨진 pred와 curr가 제거되지 않았는지, pred와 curr 사이에 다른 노드가 끼어들지 않았는지
29
pred->next == curr 이면 다른 노드가 끼어들지 않은 것이다.
30
전혀 없다. locking을 하고 검사를 하기 때문이다.
31
다시 처음부터 검색해서 원래 pred, curr로 다시 올 수 있는지 확인한다.
32
다른 스레드들이 pred와 curr를 계속 수정하는 경우 계속 재시도를 하면서 기아를 겪을 수 있다.
33
X
34
add() validate(pred, curr) false true contains() validate(pred, curr) curr.key == key
35
X
36
링크드 리스트에서 성능에 가장 영향에 미치는 것은 검색이다. 성긴 동기화는 한 번 검색하는데, 낙천적 동기화는 처음에 한 번 검색하고 validate 하면서 한 번 더 검색하기 때문에 2배 느리다.
37
스레드 개수가 늘어남에 따라 싱글 스레드보다 성능이 더 좋아지는 것을 확인했다.
38
낙천적 동기화는 코어의 개수가 많지 않으면 성능이 좋지 않다. 이것을 개선하고자 나온 것이 게으른 동기화이다.
39
validate()가 노드를 처음부터 다시 순회하지 않고 수행
40
O
41
read-only
42
marked 필드
43
먼저
44
node == pred pred.next == curr node = node.next
45
validate() !pred.marked && !curr.marked && pred.next == curr contains() curr = curr.next; curr.key == key && !curr.marked
46
curr.marked = true;
47
marking 되어 있지 않은 모든 node는 실제 리스트에 존재하는 살아있는 node이다. 해당 명제가 참이기 때문이다.
48
X
49
빨라진다. 스레드가 하나일 때는 더 느리지만 이 세상에 싱글코어 컴퓨터는 없다.
50
블럭킹 알고리즘이다., 메모리 릭이 있다.
51
재사용하면서 오동작할 수 있기 때문이다.
52
아무도 remove된 node를 가리키지 않을 때, remove 시점에서 중복실행 중인 모든 method의 호출이 종료되었을 때
53
O
54
shared_ptr을 생성할 때 reference counter도 같이 할당해서 이 카운터를 증가하고 감소하면서 0이 되면 해제한다.
55
shared_ptr 객체를 load, store하는 것이 atomic이 아니기 때문이다.
56
싱글 스레드에서만 잘 돌아가고 멀티스레드에서는 죽는다.
57
성긴 동기화, 게으른 동기화, 낙천전 동기화, 세밀한 동기화
58
게으른 동기화, 낙천적 동기화, 성긴동기화, 세밀한 동기화
59
잘 돌아간다. 하지만 성능이 엉망진창이다.
60
X
61
내부적으로 mutex를 사용하고 또한 하나의 lock으로 실행되기 때문이다.
62
local atomic shared ptr
63
X
64
X
65
X
66
if (dataReady == false) return false; temp = g_data;
67
O
68
while (true) { int old_sum = sum; int new_sum = old_sum + 2; if (true == CAS(&sum, old_sum, new_sum)) break; }
69
CAS는 2개의 변수에 동시에 적용할 수 없기 때문이다. 따라서 CAS를 두 번 나누어서 해주어야 하는데 그러면 그 사이에 다른 스레드가 끼어들 수 있다. 난이도가 상승한다.
70
critical section이 충분히 짧아졌기 때문이다.
71
pred의 marking을 확인하면서 변경해야 한다.
72
pred와 next가 curr인 것을 확인하면서 변경해야 한다.
73
한 장소에 주소와 marking을 동시에 저장했다.
74
우리의 컴퓨터가 64비트라고 그 비트를 다 사용하지 않는다. 이 중에 안 쓰는 비트를 사용하여 가능하게 한다.
75
마킹이되었지만 remove되지 않은 노드가 있는 있을 수 있다.
76
pred.next.comparedAndSet(curr, succ, false, false);
77
add() find(head, key) new AtomicMarkableReference(curr, false) pred.next.compareAndSet(curr, node, false, false) remove() find(head, key) curr.next.attemptMark(succ, true) pred.next.compareAndSet(curr, succ, false, false)
78
그냥 포인터가 아닌 합성 포인터를 사용했기 때문이다.
79
성능이 좋다. 메모리 재사용에 stopation이 생길 수 있다.
80
EPOCH
81
Epoch를 사용하기에 정밀하지 않다. 읽고 쓰는 오버헤드가 크다. 데이터 사이즈가 크다.
82
검색 대상이 아니다. 일종의 최적화 시켜주는 코드
83
어느 한 스레드의 thread epoch counter가 증가하지 않을 경우 메모리 릭이나 다름 없다.
84
O