Spin Lock VS Mutex Lock

스핀 락을 구현해서 Mutex Lock과 시간을 계산해봤는데 왜 스핀락이 더 느릴까요?

따른 더 고려해봐야 하는 사항이 있을까요?

다른 github 코드를 사용해봐도 결과는 똑같네요

P.S 코드가 없어도 코드 관련인거 같아서 카테고리를 코드로 했는데 문제가 생긴다면 알려주세요

안녕하세요.

크리티컬 섹션에 체류하는 시간이 길면 뮤텍스가 더 성능이 좋을 수도 있을것같네용

짧은시간으로 테스트해도 그러네요 ㅠㅠ 답변감사합니다.

이런건 실제로 어떤 코드를 사용했고, 테스트는 어떻게 했는지 자세히 봐야 알 수 있어서 글만으로는 모르겠읍니다

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

constexpr int          MAX_THREADS = 4;
constexpr unsigned int MAX_LOOP = 10000000;

using namespace std::chrono;
using Threads   = std::vector<std::thread>;
using mutex     = std::mutex;
void SumByLock();
void SumBySpinLock();

class SpinLock{
private:
    std::atomic_flag lck = ATOMIC_FLAG_INIT;
public:
    void lock(){
        while (lck.test_and_set(std::memory_order_acquire));
    }

    void unlock(){
        lck.clear(std::memory_order_release);
    }
};

volatile int            g_sum;
mutex                   g_mutex;
SpinLock                g_spinLock;

int main() {
    std::cout << "====number of Thread: " << MAX_THREADS << "=====\n\n\n";

#pragma region Mutex Lock
    {
        Threads     threads_;
        g_sum = 0;
        auto startTime = high_resolution_clock::now();
        for (int i = 0; i < MAX_THREADS; ++i) {
            threads_.emplace_back(SumByLock);
        }
        for (auto& thread : threads_)
            thread.join();
        auto EndTime = high_resolution_clock::now();

        auto elapsedTime = EndTime - startTime;
        std::cout << "Sum Result: " << g_sum << "\n";
        std::cout << "Elapsed Time For Lock: " <<
            duration_cast<std::chrono::milliseconds>(elapsedTime).count() << "millsec\n";
    }
#pragma endregion

#pragma region Spin Lock
    {
        Threads     threads_;
        g_sum = 0;
        auto startTime = high_resolution_clock::now();
        for (int i = 0; i < MAX_THREADS; ++i) {
            threads_.emplace_back(SumBySpinLock);
        }
        for (auto& thread : threads_)
            thread.join();
        auto EndTime = high_resolution_clock::now();

        auto elapsedTime = EndTime - startTime;
        std::cout << "Sum Result: " << g_sum << "\n";
        std::cout << "Elapsed Time For Spin Lock: " <<
            duration_cast<std::chrono::milliseconds>(elapsedTime).count() << "millsec\n\n";
    }
#pragma endregion 
}

void SumByLock() {
    for (unsigned int i = 0; i < MAX_LOOP/ MAX_THREADS; ++i) {
        g_mutex.lock();
        g_sum += 1;
        g_mutex.unlock();
    }
}

void SumBySpinLock() {
    for (unsigned int i = 0; i < MAX_LOOP/ MAX_THREADS; ++i) {
        g_spinLock.lock();
        g_sum += 1;
        g_spinLock.unlock();
    }
}

image

말씀하신 코드와 측정시간 입니다.

spinlock을 구현하기 위하여 사용한 std::atomic_flag 및 그 연산이 무거워서 이런 결과가 나온 것 같습니다.
아래 URL의 코드를 이용해서, 스핀락을 구현하면 반대의 결과가 나옵니다.


결과는 다음과 같습니다.

image

코드는 AcquireSpinLock, ReleaseSpinLock함수를 추가하고, 기존의 스핀락을 대체하였습니다.

#include<chrono>
#include<mutex>
#include<vector>
#include<atomic>
#include<thread>
#include<iostream>
#include<windows.h>

constexpr int          MAX_THREADS = 4;
constexpr unsigned int MAX_LOOP = 10000000;

using namespace std::chrono;
using Threads = std::vector<std::thread>;
using mutex = std::mutex;
void SumByLock();
void SumBySpinLock();

class SpinLock {
private:
    std::atomic_flag lck = ATOMIC_FLAG_INIT;
public:
    void lock() {
        while (lck.test_and_set(std::memory_order_acquire));
    }

    void unlock() {
        lck.clear(std::memory_order_release);
    }
};

LONG g_lSpinLock;

void AcquireSpinLock(volatile LONG* plCount)
{
    LONG    lOldCount;

lb_try:
    lOldCount = _InterlockedCompareExchange(plCount, 1, 0);

    if (lOldCount)
    {
        for (DWORD i = 0; i <1024; i++)
        {
            YieldProcessor();   // __asm pause
        }
        goto lb_try;
    }

}
void ReleaseSpinLock(volatile LONG* plCount)
{
    _InterlockedDecrement(plCount);
}

volatile int            g_sum;
mutex                   g_mutex;
SpinLock                g_spinLock;

int main() {
    std::cout << "====number of Thread: " << MAX_THREADS << "=====\n\n\n";


#pragma region Mutex Lock
    {
        Threads     threads_;
        g_sum = 0;
        auto startTime = high_resolution_clock::now();
        for (int i = 0; i < MAX_THREADS; ++i) {
            threads_.emplace_back(SumByLock);
        }
        for (auto& thread : threads_)
            thread.join();
        auto EndTime = high_resolution_clock::now();

        auto elapsedTime = EndTime - startTime;
        std::cout << "Sum Result: " << g_sum << "\n";
        std::cout << "Elapsed Time For Lock: " <<
            duration_cast<std::chrono::milliseconds>(elapsedTime).count() << "millsec\n";
    }
#pragma endregion

#pragma region Spin Lock
    {
        Threads     threads_;
        g_sum = 0;
        auto startTime = high_resolution_clock::now();
        for (int i = 0; i < MAX_THREADS; ++i) {
            threads_.emplace_back(SumBySpinLock);
        }
        for (auto& thread : threads_)
            thread.join();
        auto EndTime = high_resolution_clock::now();

        auto elapsedTime = EndTime - startTime;
        std::cout << "Sum Result: " << g_sum << "\n";
        std::cout << "Elapsed Time For Spin Lock: " <<
            duration_cast<std::chrono::milliseconds>(elapsedTime).count() << "millsec\n\n";
    }
#pragma endregion 
}

void SumByLock() {
    for (unsigned int i = 0; i < MAX_LOOP / MAX_THREADS; ++i) {
        g_mutex.lock();
        g_sum += 1;
        g_mutex.unlock();
    }
}

void SumBySpinLock() {
    for (unsigned int i = 0; i < MAX_LOOP / MAX_THREADS; ++i) {
        //g_spinLock.lock();
        AcquireSpinLock(&g_lSpinLock);
        g_sum += 1;
        ReleaseSpinLock(&g_lSpinLock);
        //g_spinLock.unlock();
    }
}


1 Like

감사합니다! :+1: