본문 바로가기

MFC(Window Programming)

Mutex, Semaphore, Critical Section 동기화 메커니즘 성능 비교

728x90

멀티스레드 프로그래밍에서 Mutex, Semaphore, 그리고 Critical Section은 동기화 메커니즘의 대표적인 예입니다. 이 글에서는 각 메커니즘의 성능 차이를 C++ 프로그램을 통해 비교하고, 동시에 프로그램의 진행 상태를 확인할 수 있도록 진행 바(progress bar)를 추가하는 방법을 설명합니다.

1. C++ 코드: Mutex, Semaphore, Critical Section 비교

아래 코드는 Mutex, Semaphore, Critical Section을 각각 사용하여 공유 자원에 접근하고, 성능을 비교하는 예제입니다. 또한 프로그램의 진행 상태를 실시간으로 관찰할 수 있도록 1%마다 진행 바를 출력하도록 하였습니다.


// C++ 코드
#include <iostream>
#include <thread>
#include <mutex>
#include <semaphore.h>
#include <atomic>
#include <chrono>
#include <iomanip> // for std::setw

const int NUM_THREADS = 4;
const int NUM_ITERATIONS = 1000000;
int counter = 0; // 공유 자원

// Mutex
std::mutex mtx;

// Semaphore (POSIX semaphore)
sem_t sem;

// Critical Section (using atomic operation for comparison)
std::atomic<int> atomic_counter(0);

// Progress bar function
void print_progress(int current, int total) {
    int bar_width = 70;
    float progress = (float)current / total;
    std::cout << "[";
    int pos = bar_width * progress;
    for (int i = 0; i < bar_width; ++i) {
        if (i < pos)
            std::cout << "=";
        else if (i == pos)
            std::cout << ">";
        else
            std::cout << " ";
    }
    std::cout << "] " << int(progress * 100.0) << " %\r";
    std::cout.flush();
}

// Shared resource access function for Mutex
void increment_with_mutex() {
    for (int i = 0; i < NUM_ITERATIONS; ++i) {
        mtx.lock();
        ++counter;
        mtx.unlock();
        if (i % (NUM_ITERATIONS / 100) == 0) {
            print_progress(i, NUM_ITERATIONS);  // Print progress every 1%
        }
    }
}

// Shared resource access function for Semaphore
void increment_with_semaphore() {
    for (int i = 0; i < NUM_ITERATIONS; ++i) {
        sem_wait(&sem);
        ++counter;
        sem_post(&sem);
        if (i % (NUM_ITERATIONS / 100) == 0) {
            print_progress(i, NUM_ITERATIONS);  // Print progress every 1%
        }
    }
}

// Shared resource access function for atomic (Critical Section)
void increment_with_atomic() {
    for (int i = 0; i < NUM_ITERATIONS; ++i) {
        ++atomic_counter;
        if (i % (NUM_ITERATIONS / 100) == 0) {
            print_progress(i, NUM_ITERATIONS);  // Print progress every 1%
        }
    }
}

int main() {
    // Semaphore initialization
    sem_init(&sem, 0, 1);

    // Measure execution time for Mutex
    counter = 0;
    auto start_time = std::chrono::high_resolution_clock::now();
    std::thread threads_mutex[NUM_THREADS];
    std::cout << "Running Mutex Test..." << std::endl;
    for (int i = 0; i < NUM_THREADS; ++i) {
        threads_mutex[i] = std::thread(increment_with_mutex);
    }
    for (int i = 0; i < NUM_THREADS; ++i) {
        threads_mutex[i].join();
    }
    auto end_time = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> mutex_time = end_time - start_time;
    std::cout << "\nMutex time: " << mutex_time.count() << " seconds, counter: " << counter << std::endl;

    // Measure execution time for Semaphore
    counter = 0;
    start_time = std::chrono::high_resolution_clock::now();
    std::thread threads_semaphore[NUM_THREADS];
    std::cout << "Running Semaphore Test..." << std::endl;
    for (int i = 0; i < NUM_THREADS; ++i) {
        threads_semaphore[i] = std::thread(increment_with_semaphore);
    }
    for (int i = 0; i < NUM_THREADS; ++i) {
        threads_semaphore[i].join();
    }
    end_time = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> semaphore_time = end_time - start_time;
    std::cout << "\nSemaphore time: " << semaphore_time.count() << " seconds, counter: " << counter << std::endl;

    // Measure execution time for Critical Section (atomic)
    atomic_counter = 0;
    start_time = std::chrono::high_resolution_clock::now();
    std::thread threads_atomic[NUM_THREADS];
    std::cout << "Running Atomic Test..." << std::endl;
    for (int i = 0; i < NUM_THREADS; ++i) {
        threads_atomic[i] = std::thread(increment_with_atomic);
    }
    for (int i = 0; i < NUM_THREADS; ++i) {
        threads_atomic[i].join();
    }
    end_time = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> atomic_time = end_time - start_time;
    std::cout << "\nAtomic time: " << atomic_time.count() << " seconds, counter: " << atomic_counter.load() << std::endl;

    // Cleanup
    sem_destroy(&sem);
    
    return 0;
}

2. 컴파일 및 실행 방법

아래 명령어로 코드를 컴파일하고 실행할 수 있습니다:


g++ -std=c++11 -pthread -o sync_test_with_progress sync_test_with_progress.cpp
./sync_test_with_progress

프로그램이 실행되는 동안 0%부터 100%까지의 진행 상태를 실시간으로 관찰할 수 있습니다.

3. 결론

이 포스팅에서는 Mutex, Semaphore, Critical Section을 사용하여 공유 자원을 보호하는 방법과 이들의 성능 차이를 비교하는 방법을 설명하였습니다. 각 동기화 메커니즘은 서로 다른 성능 특성을 가지며, 이를 사용함에 있어 상황에 맞는 선택이 필요합니다. 진행 상태를 출력하는 기능을 추가함으로써 프로그램의 동작을 실시간으로 확인하는 방법도 소개하였습니다.

 

728x90