개요
지난번에 쓰레드와 프로세스, 멀티쓰레드를 사용한 병렬처리, 그리고 컨텍스트 스위칭에 대해 알아보았습니다.
병렬처리를 하여 데이터를 처리할 시 처리 시간을 줄일수 있다는 장점이 있지만, 쓰레드는 결국 하나의 객체입니다.
프로그래밍을 할 시에도 객체 관리를 위해 객체를 재사용하는 디자인 패턴이나 SOLID원칙등 이 고려되었습니다.
그렇다면 멀티쓰레딩을 할때 어떠한 방법이 있는지, 그리고 CPU가 보유한 코어에 적합한 쓰레드의 개수, 재사용 법을 알아보겠습니다.
Thread Pool
정의
Thread Pool 이란 쓰레드도 결국 하나의 객체이며 데이터 처리를 위해 들어오는 요청마다 쓰레드 객체를 계속 생성하면
쓰레드 생성에 소요되는 시간에 의해 요청 처리가 더 오래걸리며 또한 오버헤드의 누적, 메모리가 고갈되는 문제점이 있습니다.
이러한 문제를 방지하기위해 쓰레드 객체를 재사용하기위해 미리 생성된 쓰레드의 집합으로, 작업 처리를 효율적으로 관리할 수 있는 기법입니다.
Thread Pool의 특징
쓰레드 풀은 미리 생성된 쓰레드들의 집합이며, 미리 생성된 쓰레드들을 재사용 하는 방식으로 구성되어있습니다.
또한 쓰레드 풀은 들어오는 요청(Task)들을 쓰레드 풀 내부에 선입선출(FIFO) 구조로 받아오는 TaskQueue가 있으며, 해당 Queue에서 먼저 들어온 요청을 각각 쓰레드풀내부의 Run상태가 아닌 쓰레드들에게 할당하는 방식입니다.
이러한 기법으로 인해 스레드가 무제한으로 생성되는 것을 방지하여 재사용을 통한 메모리 관리, 보다 적은 오버헤딩이라는 장점이 있습니다.
Thread Pool외에도 TCP Connection을 할 시에 미리 Collection을 만들어 미리 초대할수있도록 준비하는 Collection Pool,
프로세스를 여러개 만들어서 사용하는 Process Pool 같은 방식이 있습니다.
또한 Thread Pool에 사용할 스레드들의 개수를 설정할 수 있는데, 설정 하는 도중 가장 최적의 스레드의 개수를
알 수 있는 수학적 공식이 있습니다.
Thread Pool에 생성할 스레드 개수에 대한 수학적 공식
- 적정 스레드 개수 = CPU 코어의 수 * ( 1 + 대기시간 / 작업시간 )
자바에서 미리만들어 둔Thread Pool API
자바에서는 ExecutorService 클래스는 Executor.newFixedThreadPool(int nThread)생성자를 통하여 스레드 풀을 생성 할 수있는데, 스레드의 개수는 제한이 되지만, Task Queue구조에 받아오는 Queue개수에는 제한이 없습니다.(Integer MAX_VALUE)
Queue에 제한이 없다는 것은 곧 들어오는 요청을 무한하게 받을수 있다는 문제가 있으며 이것을 예시로 알려드리겠습니다.
패스트 푸드점에 손님들이 각각 A,B,C,AA,BB,AC 이런 형태의 음식을 주문(Task)하였는데 직원(Thread)들은 한정되어 있어 시간이 늦게 들어온 주문(Task)은 조리 완성의 속도가 비교적 느리며, 또한 포장을 담당하는 직원(Queue)가 들어온 주문을 잘못 주는 심각한 문제가 생길수도 있습니다.
이러한 예시를 통해 쓰레드 풀의 쓰레드처럼 내부 Queue의 받아오는 요청 개수도 조절 하는것 방법이 더 효율적으로 ThreadPool을 사용 할 수 있습니다.
리틀의 법칙
정의
스레드의 개수가 지연시간이나 처리량(시스템이 처리 가능한 처리량)에 미치는 영향을 계산하기 위해 만든 수식입니다.
L = λ * W
L : 동시에 처리되는 요청의 개수
λ : 시스템이 처리 가능한 평균 처리량 (스레드 풀의 할당된 스레드의개수)
W : 평균 요청 처리 시간
Ex) 평균 처리량(스레드)는 15개, 평균 요청 처리시간은 66ms다
-> 15(평균 처리 스레드) / 0.066(1초) = 227(동시에 처리 될 수 있는 요청의 개수)
즉, 동시에 처리할 수 있는 요청의 개수는 227인것을 알 수 있습니다.
리틀의 법칙은 스레드 풀을 사용하여 처리를 하면 처리 시간마다 요청이 처리되는 평균 개수를 알 수 있으며,
리틀의 법칙을 통해 내부 큐에 들어올 수 있는 요청의 개수를 제한 할 수 있습니다.
암달의 법칙(Amdahl's Law)
정의
Thread Pool에서 적정 스레드의 개수, 암달의 법칙을 사용하여 스레드가 평균 요청 시간동안 처리할 수 있는 요청의 개수를 알 수 있었습니다. 암달의법칙은 병렬화 작업 성능의 향상 가능성을 예측하며, 작업의 한계를 설명하게 위해 만들어진
수학적 수식입니다.
해당 그래프를 빗대어 알 수 있듯이, 프로세서가 2^2 형식으로 늘어나며, 병렬화의 비율이 높아져도 처리속도의 증가량이 점점 줄어드는것을 볼 수 있습니다.
이것을 수학적 수식으로 표현 할 수있습니다.
수식 : S(n) = 1 / [(1 - P) + (P / n)]
- S(n) = n개의 프로세서를 사용할 때 전체 시스템의 성능 향상 비율.
- P = 병렬화 가능한 작업의 비율
- n = 사용되는 프로세서의 수
- (1 - P) = 병렬처리가 불가능한 부분
이러한 그래프와 수학적 수식을 통해 병렬화 처리 및 프로세서를 최대한으로 사용하는것이 효율적인 방법은 아니며,
작업의 한계를 알 수 있습니다.
정리 및 느낀점
정리
병렬처리는 데이터의 처리 속도를 줄여주지만, 병렬화를 통한 작업의 한계가 있다.
그것을 암달의 법칙을 통해 알 수 있으며, CPU의 코어의 개수에 따른 스레드의 개수에 대한 적정관계를
Thread Pool을 통해 알 수 있으며, 또한 쓰레드 풀 내부 스레드가 받아올 수 있는 요청의 개수는 리틀의 법칙을 통해 알 수 있다.
빅 데이터일수록 요청들이 많아지는데, 해당 요청을 스레드와 1+1 맵핑하여 스레드를 생성 시 생성 시간이 요청시간보다 오래걸리는 경우가 있는데, 이것을 방지하기위해 스레드를 재사용하여 생성시간을 줄여 메모리를 절약하는 방법이
Thread Pool이다.
느낀점
이번 포스팅에 수학적 수식이 3개나 나왔습니다.
스레드 풀 생성시 CPU 코어에 따른 적정 스레드의 개수를 나타내는 방법.
스레드 풀 내부에서 평균 요청처리 시간과 쓰레드의 개수에 따라 동시에 처리되는 요청의 개수.
병렬화 처리시 병렬화 비율과 프로세서개수의 적정 비율을 나타내어 작업의 한계를 알려주는 수식.
3개의 수식이 나왔지만, 서로 다른의미를 알려줍니다.
이러한 수식을 통하여 CPU코어에 따라 만들어야할 스레드의 적정 개수를 알 수있으며,
람다식이나 내부클래스같이 코드의 간결성이라는 장점이 있지만 남용하면 오히려 코드의 구독성이 줄어드는 단점이 있듯습니다.
스레드 또한 10개만으로도 처리할 수 있는일을 100개를 생성하여 처리를 시키면 90개의 스레드들의 상태가 대기중인것을 느낄 수 있는데, 이것은 물리적 메모리(RAM)의 자원이 낭비되는것을 알 수 있게 되었습니다.
병렬화 처리를 할 시에 주의점을 알 수 있게된 학습을 하였으며, 이 세개의 수식을 사용하여 적절한 스레드 생성을 통해
효율적인 데이터 관리를 하는 개발자를 지향하며 노력하겠습니다.
참고내역 및 출처
Thread Pool - https://www.youtube.com/watch?v=B4Of4UgLfWc&t=548s
Thread Pool - https://www.baeldung.com/thread-pool-java-and-guava
Little, Amdahl - https://velog.io/@ckstn0777/%EC%BB%B4%ED%93%A8%ED%84%B0%EA%B5%AC%EC%A1%B0-%EC%95%94%EB%8B%AC%EC%9D%98-%EB%B2%95%EC%B9%99
'운영체제(OS)' 카테고리의 다른 글
OSI 7계층 (0) | 2023.07.25 |
---|---|
Thread Safe란 무엇인가 (0) | 2023.07.19 |
스케줄링 알고리즘(Scheduling Algorithms) (0) | 2023.07.18 |
프로세스(ProCess), 쓰레드(Thread) (0) | 2023.07.16 |
컨텍스트 스위칭(Context Switching) (0) | 2023.07.15 |