ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Linux] 멀티 스레드와 싱글 스레드 성능 비교하기 _pthread
    Linux 2023. 4. 14. 16:33
    728x90
    반응형

    pthread를 이용해서 멀티 스레드와 싱글 스레드 성능 비교하기

     

    이번 운영체제 과제는 멀티 스레드가 싱글 스레드보다 빠르게 작업 수행이 가능하다는 것을 증명하는 코드를 구현하는 거였어요. 🤔

    아래는 과제의 본문입니다.

     

     

    교수님께서 부가적으로 말씀하신게 sleep()은 쓰지 말라고 하시더군요

     


     

    🍊 pthread와 관련된 함수

    📍 pthread_craete()

    pthread_create() : 새로운 스레드를 생성하는 함수

    #include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                       void *(*start_routine) (void *), void *arg);
    • thread: 새로운 스레드의 식별자를 저장할 변수의 포인터입니다. pthread_t 타입입니다.
    • attr: 새로운 스레드의 특성을 지정하는 pthread_attr_t 타입의 포인터입니다. 일반적으로 **NULL**을 사용하면 기본값이 사용됩니다.
    • start_routine: 새로운 스레드가 실행할 함수의 포인터입니다. 이 함수는 반드시 void * 타입의 인자를 받아서 void * 타입으로 반환해야 합니다.
    • arg: start_routine 함수에 전달될 인자를 지정하는 void * 타입의 포인터입니다.

    pthread_create() 함수는 호출되면 새로운 스레드를 생성하고, start_routine 함수를 실행합니다. start_routine 함수가 실행되면서 arg 인자를 전달받습니다.

     

    📍 pthread_join()

    pthread_join() 함수는 생성된 스레드가 종료될 때까지 대기하고, 스레드의 실행이 완료될 때까지 현재 스레드를 차단(block)합니다.

    int pthread_join(pthread_t thread, void **retval);
    • thread: 대기하고자 하는 스레드의 식별자
    • retval: 종료된 스레드가 반환하는 값에 대한 포인터

    pthread_join()의 역할

    1. 대기: pthread_join() 함수는 인자로 전달된 스레드 식별자(thread identifier)가 가리키는 스레드가 종료될 때까지 대기합니다.
    2. 스레드 반환값 수집: pthread_join() 함수는 스레드가 반환하는 값에 대한 포인터를 인자로 받습니다. 이 포인터는 종료된 스레드에서 pthread_exit() 함수를 호출할 때 전달한 값에 대한 포인터입니다. 이 함수가 반환한 값은 호출한 스레드에서 스레드의 반환값을 수집하는 용도로 사용될 수 있습니다.
    3. 스레드 자원 해제: pthread_join() 함수는 종료된 스레드의 자원을 해제합니다. 이는 스레드의 스택(stack)과 같은 자원을 해제하여, 메모리 누수를 방지하는 역할을 합니다.

    이 포인터는 종료된 스레드에서 pthread_exit() 함수를 호출하여 전달된 값을 받습니다.

     

    📍 pthread_mutex_init()

    pthread_mutex_init() 함수는 뮤텍스(mutual exclusion, 상호배제)를 초기화하는 데 사용됩니다.

    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
    • mutex: 초기화하려는 뮤텍스의 포인터입니다. 이 함수를 호출하여 뮤텍스를 초기화하면, 이 포인터가 가리키는 메모리에 뮤텍스 객체가 생성됩니다.
    • attr: 뮤텍스의 속성을 설정하는 데 사용됩니다. 이 인자를 NULL로 전달하면 기본 속성으로 뮤텍스를 초기화합니다.

     

     

    🍊 mutex와 관련된 함수

     

    📍 pthread_mutex_lock()

    pthread_mutex_lock() 함수는 상호 배제를 위해 임계 구역을 보호할 때 사용하는 함수입니다. 이 함수를 통해 뮤텍스에 잠금을 걸어 다른 스레드가 접근하지 못하도록 합니다. 인자로는 잠금을 걸려는 뮤텍스의 포인터를 전달합니다.

     

    📍 pthread_mutex_unlock()

    pthread_mutex_unlock() 함수는 pthread_mutex_lock() 함수의 반대로, 뮤텍스에 잠금을 해제시키기 위한 함수입니다. 인자로는 마찬가지로 잠금을 해제하려는 뮤텍스의 포인터를 전달합니다.

     

    📍 pthread_mutex_destroy()

    pthread_mutex_destroy() 함수는 뮤텍스(mutual exclusion, 상호배제)를 파괴하는 함수입니다. 인자로는 삭제하려는 뮤텍스 포인터를 전달합니다. 이 함수를 통해 뮤텍스 객체를 소멸시킵니다.

     

     


     

    🍊 코드로 구현해보자

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <sys/time.h>
    
    #define MAX_THREAD 8 // 스레드 개수
    #define MAX_NUM 1000000000 // 합할 숫자의 최대값
    
    /* 1부터 MAX_NUM 까지 모든 수를 합하는 동작을
     멀티스레드와 싱글스레드로 각각 실행하여 성능을 비교하는 프로그램입니다. */
    
    long sum = 0;
    long part = 0;
    pthread_mutex_t lock; // 상호 배제 보장을 위한 뮤텍스 사용
    
    typedef struct {
        long threadSum; // 합산 결굇값 담을 변수
        struct timeval timeval; // 시간 계산을 위한 timeval 변수
    } Thread; // 구조체 정의
    
    // 멀티 스레드 동작 수행을 위한 함수
    void *sum_thread(void *arg) {
        long thread_part = part++;
        long local_sum = 0;
        for (long i = thread_part * (MAX_NUM / MAX_THREAD) + 1; i <= (thread_part + 1) * (MAX_NUM / MAX_THREAD); i++) {
            local_sum += i;
        }
        pthread_mutex_lock(&lock); // 임계 구역 보호를 위해 상호 배제 lock
        sum += local_sum;
        pthread_mutex_unlock(&lock); // Mutex lock 해제
        return NULL;
    }
    
    // 싱글 스레드 실행 함수
    Thread do_single_thread() {
        struct timeval start_time, end_time; // 시간 계산을 위한 시작 & 종료 변수
    
        gettimeofday(&start_time, NULL); // 시간 측정 시작
    
        pthread_mutex_lock(&lock); // 임계 구역 잠금
        for (long i = 1; i <= MAX_NUM; i++) {
            sum += i;
        }
        pthread_mutex_unlock(&lock); // 임계 구역 해제
        gettimeofday(&end_time, NULL); // 시간 측정 종료
    
        // 구조체에 싱글 스레드 값 입력
        Thread single_thread = {
            sum, // sum 값 대입
            {.tv_sec  = end_time.tv_sec - start_time.tv_sec, .tv_usec = end_time.tv_usec - start_time.tv_usec} // 측정 시간 대입
            };
    
        // 싱글 스레드 구조체 반환
        return single_thread;
    }
    
    // 멀티 스레드 실행 함수
    Thread do_multi_thread() {
        sum = 0;
    
        // 멀티 스레드 생성
        pthread_t threads[MAX_THREAD];
        struct timeval start_time, end_time; // 시간 계산을 위한 시작 & 종료 변수
    
        gettimeofday(&start_time, NULL); // 시간 측정 시작
        
        // 스레드 8개 생성
        for (long i = 0; i < MAX_THREAD; i++) {
            pthread_create(&threads[i], NULL, sum_thread, NULL);
        }
    
        // 스레드 반환값 수집
        for (long i = 0; i < MAX_THREAD; i++) {
            pthread_join(threads[i], NULL);
        }
    
        gettimeofday(&end_time, NULL); // 시간 측정 종료
    
        // 구조체에 멀티 스레드 값 입력
        Thread multi_thread = {
            sum, // sum 값 대입
            { .tv_sec  = end_time.tv_sec - start_time.tv_sec, .tv_usec = end_time.tv_usec - start_time.tv_usec } // 측정 시간 대입
            };
    
        // 멀티 스레드 구조체 반환
        return multi_thread;
    }
    
    // 결과 출력 함수
    void print_diff(Thread single_thread_processing_time, Thread multi_thread_processing_time){
        printf("single_thread:\\n");
        printf(" - 결과: %ld, 소요시간: %ld.%3d 초\\n\\n", single_thread_processing_time.threadSum, single_thread_processing_time.timeval.tv_sec, single_thread_processing_time.timeval.tv_usec);
        printf("multi_thread:\\n");
        printf(" - 결과: %ld, 소요시간: %ld.%3d 초", multi_thread_processing_time.threadSum, multi_thread_processing_time.timeval.tv_sec, multi_thread_processing_time.timeval.tv_usec);
    }
    
    int main(int argc, char* argv[]) {
        pthread_mutex_init(&lock, NULL); // 뮤텍스 초기화
        Thread single_thread_processing_time = do_single_thread(); // 싱글 스레드 함수 실행
        Thread multi_thread_processing_time = do_multi_thread(); // 멀티 스레드 함수 실행
    
        // 결과 출력
        print_diff(single_thread_processing_time, multi_thread_processing_time);
    
        pthread_mutex_destroy(&lock); // 사용이 끝난 뮤텍스 제거
        return 0;
    }
    

     

     


    🍊 후기

    조금의 시행착오가 있었지만 무사히 끝냈습니다. 역시 코딩은 재미있어요 ㅎㅅㅎ.

    그리고 이번에 또 느낀 건 과제가 아니더라도 주석은 항상 달도록 습관으로 해둬야겠다고 생각했어요. 누구나 제 코드륵 술술 읽을 수 있도록 말이죠 🤔

    LIST
Designed by Tistory.