Thread

김현송·2023년 4월 24일
0

제가 기존에 알고있는 쓰레드는 웹 브라우저가 js의 싱글쓰레드를 멀티쓰레드처럼 지원한다 정도로만 알고 있었습니다.

쓰레드를 구현하기에 앞서 최적화가 무엇일까요?

최적화

최적화란 무엇일까요?

최적화는 특정한 목적을 달성하기 위해 계획을 코드로 실현하는 일을 최적화라 합니다.
즉 최적화는 목적에 따라 달라질 수 있다는 의미입니다.

최적화의 목적

  • 최대 성능
  • 최소 리소스 -> 최대 효율
  • 최소 리소스 -> 최대 효율 + 최대 성능 (?)

주로 장비에 사용되는 임베디드 시스템의 경우 2번째를 최적화의 목적으로 둔다고 합니다.
보통 작은 소형 장비들의 경우 파워를 (저전력) 아껴쓰는 경우가 많기 때문입니다.

리눅스가 하드웨어들을 관리하는 방법

리눅스는 오픈 소스이기때문에 각 subsystem들을 새로운 버전에 합치는 방식으로 관리합니다.

Scheduling

프로세스가 실행될 때 필요한 시스템의 자원들을 해당 프로세스에 할당하는 작업을 스케줄링이라 합니다.

예를들어 프로세스 1이 5GB의 메모리를 차지하고 있고 프로세스 2가 30MB를 사용하고 있다고 가정합니다.(멀티 프로세스의 경우)
두 프로세스 중 어떤 프로세스에게 메모리를 먼저 할당해야 할까요?
해당 우선순위를 결정하는 것이 Scheduler 입니다.

Thread

CPU로 부터 할당받은 메모리를 가지고 프로세스 내에서 실제로 작업하는 사람(?) 객체가 쓰레드입니다.

구현

최적화를 하기 위해서 CPU가 지금 어떤 일을 하고 있는지 실시간으로 관찰할 필요가 있습니다.

htop

리눅스 환경에서는 htop을 이용해 실시간으로 관찰이 가능합니다.

숫자 0, 1, 2, 3이 가리키는 것은 각 CPU 번호입니다. 쿼드코어 CPU입니다
Mem은 메모리 Swp는 스왑 메모리라해서 실제 RAM에서 부족한 자원을 disk로부터 땡겨 쓰는 것입니다.

이제 테스트를 해보겠습니다.

최소 리소스 사용

#include <stdio.h>
#include <time.h>

int main(void){
    int i = 0;
    int ret = 0;
    for (i = 0; i< 1000000000; i++){
        ret++;
    }
    return 0;
}

해당 코드의 일반적인 실행 속도는 대략 3초정도 소요됩니다.

CPU governor : 각 디바이스가 전력 소모를 어떻게 할지 정책을 결정해주는 일을 함

echo powersave > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

하지만 CPU의 상태를 ondemand (사용자가 요구할때마다 메모리를 주는 것이 아닌 ) powersave 모드로 변경하면

최소 리소스를 통해 프로그램을 실행합니다. (속도는 4배 넘게 차이가 납니다)

멀티 쓰레드를 통한 최대 성능

아래 테스트 코드를 실행하면

#include <stdio.h>
#include <pthread.h>

#define NUM_THREADS 2
#define MAX_NUM 10000000000

void *summing(void *arg){
    int start = 0;
    int end = start + MAX_NUM / THREADS;
    int sum = 0;
    for (int i =start; i<end; i++){
        sum+= 1;
    }
    printf("sum %d\n", sum);
    pthread_exit(NULL);
}


int main(void){
    pthread_t threads[NUM_THREADS];
    int thread_args[NUM_THREADS];
    int i;
    
    for (i = 0; i < NUM_TRHEADS; i++){
        pthread_create(&threads[i], NULL, summing, &thread_agrs[i]);
    }
    for (i = 0; i < NUM_THREADS; i++){
        // 작업을 실행시키고 해당 작업 흐름을 기다림
        pthread_join(threads[i], NULL)
    }
    return 0;
}


최소 리소스 사용의 대략 절반정도로 시간이 단축된 것을 알 수 있습니다.
(쓰레드를 2개를 사용했을 때 기준입니다.)

4개로 늘린다면 어떻게 될까요?


줄기는 했지만 거의 비슷하다고 볼 수 있습니다. 즉, 쓰레드를 생성하고 join 하는데 드는 비용 등 고려해야할 요소가 많기 때문에 쓰레드를 무작정 늘린다고 성능을 보장받기는 어렵습니다.




여담으로 파이썬은 해당 프로그램을 실행할 때 멀티쓰레드에서는 다음과 같은 결과가 나타납니다.

첫번째는 그냥 프로그램 실행, 두번째는 멀티쓰레드를 이용한 것입니다.

Python은 언어차원에서 I/O를 제외한 프로그램은 멀티쓰레드를 지원하는것을 lock을 걸어두었다고 합니다.

profile
안녕하세요

0개의 댓글