๋ชฉ์ฐจ
- ๋ฌธ์ ์ ์
- lock free ๊ตฌํ
- ABA ํด๊ฒฐ
- intํ ๊ตฌํ(+ Hazard pointer)
- Counter
- ๊ทธ ์ธ์ ๋ฐฉ๋ฒ๋ค
- mutex lock(spin lock)๊ณผ์ ๋น๊ต
Hazard Pointer
https://m.blog.naver.com/PostView.nhn?blogId=jjoommnn&logNo=130127286459&proxyReferer=https:%2F%2Fwww.google.com%2F ๋ฅผ ์ฐธ๊ณ ํ์ฌ ์์ฑํ์๋ค.
๋ฌธ์ ์
๋จผ์ delete๋ฅผ ์ด์ฉํ์ฌ ๊ฐ์ฒด๋ฅผ ์ญ์ ํ๋ฉด,
- ํด๋น ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๊ณ ์๋ ์ค๋ ๋์ ๋ฌธ์ ๊ฐ ์๊ธธ ์๋ ์๊ณ
- ๊ฐ์ฒด๊ฐ ์ญ์ ๋ ์ฃผ์์ ๋ค์ ๊ฐ์ ๊ฐ์ฒด๊ฐ ํ ๋น๋์ด ABA ๋ฌธ์ ๊ฐ ์ผ์ด๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
- ์๋ก ๊ฐ์ฒด๋ฅผ ํ ๋นํ๋ ๊ฒ์ ์ฌ์ฉ์๊ฐ ๊ด๋ฆฌํ๋ list๊ฐ ์๋๋ค.
1๋ฒ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ Hazard pointer๋ฅผ ๊ตฌํํด๋ณด์์ผ๋, 2, 3๋ฒ ๋ฌธ์ ๋๋ฌธ์ ๊ฒฐ๊ตญ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ABA ๋ฌธ์ ์ ์จ์ ํ ํด๊ฒฐ์ฑ ์ด ๋์ง ๋ชปํ๋ค. ๊ทธ๋๋ ํค์ ๋ ํฌ์ธํฐ์ ์๋ฆฌ๋ฅผ ๊ณต๋ถํ๊ณ ์ฝ๋๋ฅผ ๊ตฌํํด๋ณด์๋ค.
์๋ฆฌ
์ฐ์ ๊ฐ ์ฐ๋ ๋๋ง๋ค ์ญ์ ๊ฐ ํ์ํ ๋ ธ๋๋ค์ ๋ด์๋๋ list๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผํ๋ค. ์ด๋ฅผ RetireList๋ผ๊ณ ํ์. ์ฐ๋ ๋๋ค์ pop์ ํ ๋๋ง๋ค ํ์์์ด์ง ๋ ธ๋๋ค์ ์ญ์ ํ๊ธฐ ์ํด RetireList์ ๋ด์๋๋ค. ํ์ง๋ง ์ด ๋ ธ๋๋ค์ด ์ฐธ์กฐ๋์ง ์์ ๋ ์ญ์ ํด์ผํ๋ฏ๋ก ๋ชจ๋ ์ฐ๋ ๋๊ฐ ๊ณต์ ํ๋ Hazard pointer๋ฅผ ๋ง๋ ๋ค.
Hazard pointer๋ ์ฐ๊ฒฐ๋ฆฌ์คํธ ํํ๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉฐ ์ด๋ค ๋ ธ๋๊ฐ ์ฐธ์กฐ๋๊ณ ์๋์ง๋ฅผ ๋ํ๋ด๋ ์ญํ ์ ํ๋ค. Hazard pointer์ ๊ฐ ๊ณต๊ฐ์ ํ์ฌ ํด๋น ๊ณต๊ฐ์ด ์ฌ์ฉ๋๊ณ ์๋์ง๋ฅผ ๋ํ๋ด๋ active ๋ณ์์ ์ฌ์ฉํ๊ณ ์๋ ๋ ธ๋๋ฅผ ๊ฐ๋ฆฌํค๋ node ๋ณ์, ๊ทธ๋ฆฌ๊ณ ๋ค์ ๊ณต๊ฐ์ ๊ฐ๋ฆฌํค๋ next ๋ณ์๋ก ๊ตฌ์ฑ๋์ด์๋ค.
๋จผ์ ๊ฐ ์ค๋ ๋๋ pop ์ Hazard pointer์ ๊ณต๊ฐ์ ํ๋ ๋ถ์ฌ๋ฐ๋๋ค. ๊ณต๊ฐ์ ๋ถ์ฌ ๋ฐ์ ๋์๋ ํ์ฌ active๊ฐ ์๋ ๊ณต๊ฐ์ด ์๋ ์ง ํ์ธํ๊ณ , ์๋ค๋ฉด ํด๋น ๊ณต๊ฐ์ ์ฌ์ฌ์ฉํ๋ค. (์ด๋ ๊ฒ ํ์ง ์์ผ๋ฉด ๊ณต๊ฐ์ด ๊ณ์ ๋์ด๋ ์ํ์๊ฐ์ด ๋งค์ฐ ์ค๋ ๊ฑธ๋ฆฐ๋ค.) ๊ณต๊ฐ์ ํ ๋น ๋ฐ์ ํ์๋ ํด๋น ๊ณต๊ฐ์ active ๋ณ์๋ฅผ 1๋ก ๋ง๋ค์ด ๊ณต๊ฐ์ ์ฌ์ฉํ๊ฒ ๋ค๊ณ ํ์ํ๊ณ ์ฌ์ฉํ ๋ ธ๋(list์์ popํ ๋ ธ๋)๋ฅผ node ๋ณ์์ ๋ฃ๋๋ค. ์ด๋ฌํ ์ฝ๋๋ ๋ค๋ฅธ ์ค๋ ๋์๊ฒ ์ด node๋ ํ์ฌ ์ฌ์ฉ ์ค์ด๋ ์ญ์ ํ์ง ๋ง๋ผ๊ณ ํ์ํ๋ ์ญํ ์ ํ๋ค.
tmp_next๋ฅผ ํ ๋นํ๊ณ popํ data๋ฅผ ์ ์ฅํ ๋ค CAS๋ฅผ ์ํํ๋ค. CAS๊ฐ ์ฑ๊ณตํ๊ณ ๋์๋ ํ ๋น ๋ฐ์ Hazard pointer์ ๊ณต๊ฐ์ ๋ฐ๋ฉํ๊ณ tmp๊ฐ ๊ฐ๋ฆฌํค๊ณ ์๋ ๋ ธ๋๋ฅผ ํ์ฌ ์ฐ๋ ๋์ RetireList์ ๋ด๋๋ค.
RetireList์ ๋ ธ๋๊ฐ ๋ด๊ธธ ๋๋ง๋ค Hazard pointer์ ์๋ ๋ชจ๋ ์ฌ์ฉ ์ค์ธ ๋ ธ๋๋ค์ ํ์ธํ๊ณ ์ง์ด๋ค๋ฉด ๋๋ฌด ์ค๋๊ฑธ๋ฆฌ๋ฏ๋ก RetireList์ ๋ด๊ธด ๋ ธ๋๊ฐ ํน์ ์ ์ด์์ด ๋ ๋ ํ๊บผ๋ฒ์ ๊ฒ์ฌํ๊ณ ์ญ์ ํ๊ธฐ๋ก ํ์. (์ค์ GC๋ ์ธ์ ์คํ๋ ์ง ์ฌ์ฉ์๊ฐ ์ ์ ์๋ค.) ์ฌ๊ธฐ์ RetireList๋ ์ฐ๋ ๋๋ง๋ค ํ๋์ฉ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ๋๊ธฐํํ ํ์๊ฐ ์๋ค. ๋ฐ๋ผ์ ์ฝ๋์์๋ vector ์๋ฃ๊ตฌ์กฐ๋ก ๊ตฌํํ์๋ค.
์๋ฌธ์
๊ทธ๋ ๋ค๋ฉด Hazard Pointer๋ ์ฐ๋ ๋๊ฐ ๊ณต์ ํ๋ ์์์ธ๋ฐ ์ํธ๋ฐฐ์ ๊ฐ ํ์์์๊น? atomic ๋ณ์๋ฅผ ์ด์ฉํด์ CAS์ฐ์ฐ์ผ๋ก active setting์ ํ๊ธฐ ๋๋ฌธ์ lock-free๋ก ๊ตฌํ๊ฐ๋ฅํ๋ค.
๊ทธ๋ ๋ค๋ฉด ABA ๋ฌธ์ ๋ ์ ์๊ธธ๊น? Hazard Pointer๋ pushํ๋ ์ฝ๋๋ง ์์ง popํ๋ ์ฝ๋๊ฐ ์๋ค. ์ฐ๋ ๋๋ค์ ๋น๊ณต๊ฐ์ ์ฐพ์์ ํ ๋นํ๊ณ ์์ผ๋ฉด ๊ณต๊ฐ์ ์๋ก ํ๋ ๋ง๋ ๋ค. ํ ๋น๋ฐ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ฐ๋ฉํ๋ ๊ฒ๋ ์ฌ์ค์ Hazard Pointer์ ๊ณต๊ฐ ์์ฒด๋ฅผ ์์ ๋ ๊ฒ์ด ์๋๋ผ ํด๋น ๊ณต๊ฐ์ active ๋ณ์๋ฅผ 0์ผ๋ก ๋ง๋ค๊ณ node setting์ ์ด๊ธฐํํ๋ ๊ฒ ๋ฟ์ด๋ค. ๋ฐ๋ผ์ ABA ๋ฌธ์ ๋ ์ ์๊ธด๋ค.
์ฝ๋
์ ์ฐจ์งํฅ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋์ด์๋ค
#define _CRT_SECURE_NO_WARNINGS
#define NULL_INT -1
#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <atomic>
#include <mutex>
// #include <ctime>
#include <random>
#include <intrin.h>
using namespace std;
struct Node {
int data;
Node* next_node;
};
int node_n;
atomic<Node*> free_list;
atomic<Node*> head_list;
//์ถ๋ ฅ์ ์ํ mutex
mutex mut;
//random ๊ตฌํ
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(0, 10);
// Hazard pointer ๊ตฌํ
struct HzPRec {
atomic<int> active;
Node* node;
HzPRec* next;
};
atomic<HzPRec*> pHpHead;
vector<Node*> RetireList[3];
atomic<int> DWORD = 0;
// Hazard pointer ํจ์
HzPRec* AllocHp() {
HzPRec* p;
for (p = pHpHead.load(); p != nullptr; p = p->next) { // ํ์ฌ ๋น๊ณต๊ฐ์ด ์๋์ง ํ์ธ
int active;
active = p->active.load();
if (active == 0) {
if (atomic_compare_exchange_weak(&(p->active), &active, 1))
return p;
}
}
// ์์ผ๋ฉด ์๋ก HP ๋
ธ๋ ํ๋ ๋ง๋ค๊ธฐ
p = new HzPRec();
p->active = 1;
p->node = nullptr;
// ์ฝ์
=> aba ์์ผ์ด๋๋ ์ด์ ๋ ์ญ์ ๊ฐ ์๊ธฐ ๋๋ฌธ
HzPRec* oldHead;
do {
oldHead = pHpHead.load();
p->next = oldHead;
} while (!atomic_compare_exchange_weak(&pHpHead, &oldHead, p));
return p;
}
void ReleaseHp(HzPRec* p) { // ๋ฏธ์ฌ์ฉ์ค์ผ๋ก ๋ง๋ค๊ธฐ
p->active = 0;
p->node = nullptr;
}
void Scan(int t_n) {
vector<Node*> hpList;
for (HzPRec* p = pHpHead.load(); p != nullptr; p = p->next) {
if (p->node != NULL)
hpList.push_back(p->node);
}
sort(hpList.begin(), hpList.end());
Node* p = RetireList[t_n].back();
RetireList[t_n].pop_back();
// thread๋ง๋ค ๊ฐ๊ฐ ๊ฐ์ง๊ณ ์๋ ์๋ฃ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์ RetireList๋ ์ํธ๋ฐฐ์ ํ์์์
Node* tmp;
int index = 0;
while (p != nullptr) { // RetireList๋ฅผ ํ๋์ฉ ๊บผ๋ด๋ฉด์ ํ์ธ
int flag = 0;
for (int i = 0; i < hpList.size(); i++) {
if (hpList[i] == NULL)
continue;
if (hpList[i]->data == p->data) { // ํ์ฌ ์ฌ์ฉ์ค์ธ ๋
ธ๋์ธ๊ฐ?
flag = 1;
break;
}
}
if (!flag) {
// ๋
ธ๋๊ฐ ์ฌ์ฉ ์ค์ด ์๋๋ผ๋ฉด
delete p; // ๋ฉ๋ชจ๋ฆฌ ๋ฐ๋ฉ
if (index < RetireList[t_n].size() - 1) {
p = RetireList[t_n].back(); // ๋ค์ ๋
ธ๋ ๊ฒ์ฌ
RetireList[t_n].pop_back();
}
else
p = nullptr;
}
else {
// ๋
ธ๋๊ฐ ์ฌ์ฉ ์ค์ด๋ผ๋ฉด
if (index < RetireList[t_n].size() - 1) {
p = RetireList[t_n].back();
RetireList[t_n].pop_back();
}
else
p = nullptr;
}
}
}
void RetireNode(Node* node, int t_n) {
RetireList[t_n].push_back(node);
if (RetireList[t_n].size() > 200) { // ๋
ธ๋๊ฐ 200๊ฐ ์ด์์ผ ๋ ๋ฉ๋ชจ๋ฆฌ ๋ฐ๋ฉ
Scan(t_n);
}
}
// push, pop ํจ์, print ํจ์
void printNodes(int type) { // list ์ถ๋ ฅ ํจ์
Node* tmp;
if (type == 0) {
cout << "head_list ";
tmp = head_list.load();
}
else {
cout << "free_list ";
tmp = free_list.load();
}
int num = 0;
while (tmp != nullptr) {
//cout << tmp->data << " ";
tmp = tmp->next_node;
num++;
if (num > 100000) { // error ์ฒ๋ฆฌ
cout << "node is more than 100000" << endl;
return;
}
}
cout << endl;
cout << num << endl;
}
void push(int data) {
Node* tmp_node = new Node();
tmp_node->data = data;
Node* tmp;
do {
tmp = head_list.load();
tmp_node->next_node = tmp;
} while (!atomic_compare_exchange_weak(&head_list, &tmp, tmp_node));
}
void push_f(int data) {
Node* tmp_node = new Node();
tmp_node->data = data;
Node* tmp;
do {
tmp = free_list.load();
tmp_node->next_node = tmp;
} while (!atomic_compare_exchange_weak(&free_list, &tmp, tmp_node));
}
int pop(int t_n) {
Node* tmp; Node* tmp_next;
HzPRec* hp = AllocHp(); // ๋ฉ๋ชจ๋ฆฌ ํ ๋น
while (true) {
if ((tmp = head_list.load()) == nullptr) {
ReleaseHp(hp); // ๋ฉ๋ชจ๋ฆฌ ํด์
return NULL_INT;
}
// head_list์ data๊ฐ ๋ค์ด์ฌ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค.
hp->node = tmp;
if (tmp != head_list.load()) // tmp_next ์ ์ ๋ค์ ํ ๋ฒ ํ์ธ
continue;
tmp_next = tmp->next_node;
int data = tmp->data;
if (atomic_compare_exchange_weak(&head_list, &tmp, tmp_next)) {
ReleaseHp(hp); // ๋ฉ๋ชจ๋ฆฌ ํด์
RetireNode(tmp, t_n); // ์ญ์ ํ ๋
ธ๋์ ๋ฑ๋ก
return data;
}
}
}
int pop_f(int t_n) {
Node* tmp; Node* tmp_next;
HzPRec* hp = AllocHp();
while (true) {
if ((tmp = free_list.load()) == nullptr) {
ReleaseHp(hp);
return NULL_INT;
}
// head_list์ data๊ฐ ๋ค์ด์ฌ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค.
hp->node = tmp;
if (tmp != free_list.load())
continue;
tmp_next = tmp->next_node;
int data = tmp->data;
if (atomic_compare_exchange_weak(&free_list, &tmp, tmp_next)) {
ReleaseHp(hp);
RetireNode(tmp, t_n);
return data;
}
}
}
void subThread() {
// random n, m
int n = dis(gen);
int m = dis(gen);
int thread_num = DWORD;
DWORD.fetch_add(1);
mut.lock();
cout << n << " " << m << endl;
mut.unlock();
int num = 10000;
while (num--) {
for (int i = 0; i < n; i++)
{
// free_list์ ์์ผ๋ฉด head_list์ ์ฎ๊ธฐ๋ ์์
int tmp = pop_f(thread_num);
if (tmp == NULL_INT)
continue;
push(tmp);
}
for (int i = 0; i<m; i++) {
// head_list์ ์์ผ๋ฉด free_list์ ์ฎ๊ธฐ๋ ์์
int tmp = pop(thread_num);
if (tmp == NULL_INT)
continue;
push_f(tmp);
}
}
}
int main() {
// ๋
ธ๋ ์์ฑ ๋ฐ free_list ์ฝ์
node_n = 100000; // ๋
ธ๋ ๊ฐฏ์
for (int i = node_n - 1; i >= 0; i--) {
push_f(i);
}
// ์ค๋ ๋ ์์ฑ ๋ฐ ๋์ ์์
vector<thread> threads;
int n = 3; // ์ค๋ ๋ ๊ฐฏ์
for (int i = 0; i < n; i++) {
RetireList[i] = (vector<Node*>());
threads.emplace_back(subThread);
}
for (auto &t : threads)
t.join();
threads.clear();
printNodes(0);
printNodes(1);
cin >> n;
}
๊ฒฐ๊ณผ
- ์คํ ์กฐ๊ฑด
๋ ธ๋ ๊ฐฏ์ | thread | while๋ฌธ ์ํํ์ |
100,000๊ฐ | 3๊ฐ | 10,000ํ |
- ๊ฒฐ๊ณผ
๊ตฌ๋ถ | free_list ๋ ธ๋ ์ | head_list ๋ ธ๋ ์ |
1 | 100,000 | 0 |
2 | 50,004 | 49,996 |
3 | 40,000 | 60,000 |
๋๋ ค๋ณธ ๊ฒฐ๊ณผ ์ ๋์ํ์ง๋ง ์์์ ์ธ๊ธํ๋ ๋ฌธ์ ๋ค ๋๋ฌธ์ Hazard pointer๋ ์จ์ ํ ํด๊ฒฐ์ฑ ์ด ๋์ง ๋ชปํ๋ค. ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฐพ์๋ณด์.
=> Counter ๋ฐฉ๋ฒ์ ๋ค์ ํฌ์คํ ์
Comment