[c++] CAS ๊ตฌํ˜„ ๋ฐ ABA ๋ฌธ์ œ ํ•ด๊ฒฐ :: mutex(spin lock)์™€์˜ ๋น„๊ต

Mutex์™€์˜ ๋น„๊ต

์ฝ”๋“œ

#include <iostream> 
#include <thread>
#include <vector> 
#include <atomic>
#include <random>
#include <mutex>

using namespace std;

class Node {
public:
    int value;
    Node* next;
    Node() : value(0) { next = NULL; }

    Node(int k_value) {
        next = NULL;
        value = k_value;
    }
};

int node_n;

//random ๊ตฌํ˜„
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(0, 10);

//์ถœ๋ ฅ์„ ์œ„ํ•œ mutex
mutex mut;

class LFStack {
private:
    Node * head;

public:
    void printNodes() {
        int size = 0;
        while (head != nullptr) {
            size++;
            head = head->next;
        }
        cout << size << endl;
    }

    void push(Node* newNode) {
        Node* oldHead;

        mut.lock();
        oldHead = head;
        newNode->next = oldHead;
        head = newNode;
        mut.unlock();
    }
    Node* pop() {
        Node* oldHead; Node* newHead;

        mut.lock();
        if ((oldHead = head) == nullptr) {
            mut.unlock();
            return nullptr;
        }
        newHead = oldHead->next;
        head = newHead;
        mut.unlock();
        return oldHead;
    }
};

LFStack* FreeList;
LFStack* HeadList;

void subThread() {
    // random n, m
    int n = dis(gen);
    int m = dis(gen);

    int num = 10000; // while๋ฌธ ๋ฐ˜๋ณต ํšŸ์ˆ˜
    while (num--) {
        for (int i = 0; i < n; i++)
        {
            // free_list์— ์žˆ์œผ๋ฉด head_list์— ์˜ฎ๊ธฐ๋Š” ์ž‘์—…
            Node* tmp = FreeList->pop();
            if (tmp == nullptr)
                continue;
            HeadList->push(tmp);

        }
        for (int i = 0; i<m; i++) {
            // head_list์— ์žˆ์œผ๋ฉด free_list์— ์˜ฎ๊ธฐ๋Š” ์ž‘์—…
            Node* tmp = HeadList->pop();
            if (tmp == nullptr)
                continue;
            FreeList->push(tmp);
        }
    }

}

int main() {
    // free, head list ์ƒ์„ฑ
    FreeList = new LFStack();
    HeadList = new LFStack();

    // ๋…ธ๋“œ ์ƒ์„ฑ ๋ฐ free_list์— ์‚ฝ์ž…
    node_n = 100000; // ๋…ธ๋“œ ๊ฐฏ์ˆ˜
    for (int i = node_n - 1; i >= 0; i--) {
        Node* node = new Node(i);
        FreeList->push(node);
    }

    // ์Šค๋ ˆ๋“œ ์ƒ์„ฑ ๋ฐ ๋™์ž‘ ์‹œ์ž‘ 
    vector<thread> threads;
    int n = 5; // ์Šค๋ ˆ๋“œ ๊ฐฏ์ˆ˜
    for (int i = 0; i < n; i++) {
        threads.emplace_back(subThread);
    }
    for (auto &t : threads)
        t.join();
    threads.clear();

    FreeList->printNodes();
    HeadList->printNodes();
    cin >> n;
}

 

๊ฒฐ๊ณผ

  • ์‹คํ–‰์กฐ๊ฑด
๋…ธ๋“œ ๊ฐฏ์ˆ˜ thread while๋ฌธ ์ˆ˜ํ–‰ํšŸ์ˆ˜
100,000 5 10,000
  • ๊ฒฐ๊ณผ
๊ตฌ๋ถ„ FreeList ๋…ธ๋“œ ์ˆ˜ HeadList ๋…ธ๋“œ ์ˆ˜
1 40,000 60,000
2 100,000 0
3 96,091 3,909

lock ๊ธฐ๋ฒ•์œผ๋กœ ์ƒํ˜ธ๋ฐฐ์ œ๋ฅผ ํ•ด์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฒฐ๊ณผ๋Š” ์ •ํ™•ํ•˜๊ฒŒ ์ž˜ ๋‚˜์˜จ๋‹ค.

 

์„ฑ๋Šฅ ๋น„๊ต

์‹คํ–‰ ์กฐ๊ฑด

๋…ธ๋“œ ๊ฐฏ์ˆ˜ while๋ฌธ ์ˆ˜ํ–‰ํšŸ์ˆ˜ (n,m)
100,000๊ฐœ 10,000ํšŒ ๊ฐ ์Šค๋ ˆ๋“œ์—์„œ (10,10)

๋น„๊ตํ•˜๊ธฐ ์œ„ํ•ด ์กฐ๊ฑด์„ ๋งž์ถฐ์ฃผ์—ˆ๋‹ค.

๊ตฌ๋ถ„ thread ๊ฐฏ์ˆ˜
1 10
2 100

๊ฒฐ๊ณผ 1

๊ตฌ๋ถ„ memory ์†Œ๋น„ CPU ์‚ฌ์šฉ์‹œ๊ฐ„(ms)
Counter (lock-free) 7.2MB 2755
Mutex (lock) 6.8MB 9259

<Counter>

<Mutex>

 

๊ฒฐ๊ณผ 2

๊ตฌ๋ถ„ memory ์†Œ๋น„ CPU ์‚ฌ์šฉ์‹œ๊ฐ„(ms)
Counter (lock-free) 7.1MB 29661
Mutex (lock) 12.5MB 98597

<Counter>

 

<Mutex>

 

ํ•ด์„

 Counter๋ฅผ ์ด์šฉํ•œ lock-free ๊ธฐ๋ฒ•๊ณผ Mutex๋ฅผ ์ด์šฉํ•œ lock ๊ธฐ๋ฒ•์˜ CPU ์‚ฌ์šฉ์‹œ๊ฐ„์€ 3๋ฐฐ ๊ฐ€๊นŒ์ด ์ฐจ์ด๊ฐ€ ๋‚ฌ๋‹ค. ์ด๋Š” lock ๊ธฐ๋ฒ• ์‚ฌ์šฉ ์‹œ Blocking ํ”„๋กœ๊ทธ๋žจ์ด ๋˜๋ฏ€๋กœ ์Šค๋ ˆ๋“œ๋“ค์ด lock ๋ณ‘๋ชฉํ˜„์ƒ์„ ์ผ์œผํ‚ค๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ํ•ด์„๋œ๋‹ค.

 ๋˜ํ•œ Counter๋ฅผ ์ด์šฉํ•œ ๊ธฐ๋ฒ•์€ ์Šค๋ ˆ๋“œ์˜ ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•ด๋„ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๋น„์Šทํ–ˆ๋Š”๋ฐ, Mutex ๊ธฐ๋ฒ•์€ ์Šค๋ ˆ๋“œ์˜ ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•จ์— ๋”ฐ๋ผ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๋„ ์ฆ๊ฐ€ํ•˜์˜€๋‹ค. ์ด๋Š” long long ์ •์ˆ˜ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” Counter ๊ธฐ๋ฒ•์˜ ๊ฒฝ์šฐ ๋ธ”๋ก์ด ๋๋‚˜๋ฉด ํ•ด๋‹น ๋ณ€์ˆ˜๊ฐ€ ์†Œ๋ฉธ๋˜์ง€๋งŒ, Node* ๊ฐ์ฒด ํฌ์ธํ„ฐํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” Mutex ๊ธฐ๋ฒ•์˜ ๊ฒฝ์šฐ GC๊ฐ€ ์—†๋Š” c++์—์„œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 ๊ฒฐ๋ก ์ ์œผ๋กœ lock-free ๊ธฐ๋ฒ•์€ ABA ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ณ , ๊ตฌํ˜„ ์‹œ Overhead๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค๋ฉด thread๊ฐ€ ๋ณ‘๋ ฌ๋กœ ๋งŽ์ด ๋™์ž‘ํ•˜๋Š” ์ƒํ™ฉ์—์„œ lock์˜ ๋ณ‘๋ชฉ์ด ์ผ์–ด๋‚˜๋Š” lock ์ƒํ˜ธ๋ฐฐ์ œ ๊ธฐ๋ฒ•์„ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•œ ์ข‹์€ ๋ฐฉ์•ˆ์ด ๋ ์ˆ˜์žˆ๋‹ค.

 

์ฐธ๊ณ  ์‚ฌ์ดํŠธ

https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution

http://www.cplusplus.com/reference/atomic/atomic/compare_exchange_weak/

https://felixblog.tistory.com/23

https://aerocode.net/117

https://stackoverflow.com/questions/20173766/why-does-stdatomictcompare-exchange-should-not-suffer-from-aba-issue

https://modoocode.com/270

https://www.sysnet.pe.kr/2/0/1458

https://ozt88.tistory.com/38

https://decdream.tistory.com/342

https://mountrivers.github.io/p014garbage/

https://m.blog.naver.com/jjoommnn/130129196122

https://stackoverflow.com/questions/42854116/why-does-automatic-garbage-collection-eliminate-aba-problems

https://en.wikipedia.org/wiki/ABA_problem

https://blog.naver.com/jjoommnn/130040068875?proxyReferer=https%3A%2F%2Fwww.google.com%2F

https://docs.microsoft.com/ko-kr/cpp/cpp/left-shift-and-right-shift-operators-input-and-output?view=vs-2019

https://dojang.io/mod/page/view.php?id=472

https://docs.microsoft.com/ko-kr/cpp/intrinsics/interlockedcompareexchange-intrinsic-functions?view=vs-2019

 

 

=> ๋

๋ฐ˜์‘ํ˜•