백엔드 개발자들이 알아야할 동시성 4 — 스케줄링
지난 포스팅에선 많은 쓰레드와 잦은 컨텍스트 스위칭이 어떻게 웹서버의 성능에 영향을 주는지 간단하게 이야기 해보았습니다. 이번 포스팅에선 컨텍스트 스위칭에 대해서 다시한번 이야기하며 시작해보도록 하겠습니다.
컨텍스트 스위칭 (Context Switching)
컨텍스트 스위칭이 매우 무거운 작업이긴 하지만 동시에 꼭 필요한 작업이기도 합니다. 그렇기 때문에 우리는 컨텍스트 스위칭 자체를 내려놓을수는 없고, 언제 컨텍스트 스위칭을 발생해야 가장 적은 오버헤드(Overhead)를 겪을 수 있을지에 대해 고민해보아야합니다.
선점형 스케줄링 (Preemptive Scheduling)
일반적으로 웹서버에서 사용되는 대부분의 쓰레드는 선점형 스케줄링을 이용한 쓰레드로 생성됩니다.
선점형 스케줄링은 우선순위가 더 높은, 다른 작업을 수행하기 위해 현재 진행중인 작업을 언제든 일시 중단시키고 다른 작업을 실행할 수 있는 전략의 스케줄링을 의미합니다.
즉, 선점형 스케줄링을 사용한다는것은, 내가 네트워크 I/O를 기다리지 않을 때에도 실행 권한을 빼앗길 수 있다는것을 의미합니다.
위와 같은 2개의 Task를 하나의 CPU에서 수행한다고 가정해보겠습니다.
선점형 스케줄링의 경우 다음과같이 각 Task를 중간에 멈추며 번걸아가며 수행하게 됩니다.
만약 각 Task가 실행되는 시점에 대한 상세한 수행 작업이 다음과 같다면 어떨까요?
첫 부분에선, Task1은 연산을 하던중 반복해서 컨텍스트 스위칭을 하여 연산시간을 낭비하고, 후반부에는 Task2의 연산이 컨텍스트 스위칭으로 낭비가 됩니다.
선점형 스케줄링은 하나의 Task가 전체 리소스를 차지하지 않도록 방지해주는 이점을 가지고있지만, 컨텍스트 스위칭으로 인한 오버헤드가 발생할 수 밖에 없는 단점을 가지고 있기도 합니다.
일반적인 환경에선 Task가 어느정도의 리소스를 차지할지 알기 어렵기 때문에 선점형 스케줄링을 사용하게 됩니다. CPU리소스를 하나의 Task가 독점하게 하지 않기 위한 선택이지요.
협력적 스케줄링 (Cooperative Scheduling)
협력적 스케줄링은 하나의 Task가 실행이 된다면 Task가 끝날때까지 다른 Task로 넘어가지 않는 방식의 스케줄링을 이야기합니다.
위와같이 협력적 스케줄링은 생성된 순서대로 Task를 처리하며 Task를 중간에 다른 Task로 넘어가지 않습니다. 이러한 전략은 단위가 큰 Task를 여러가지 실행하는데에 불리한면이 있지만 작은 단위의 많은 Task를 실행하는데에는 적합한 특징을 가지고 있습니다.
예를 들어 많은 양의 I/O가 있는 작업은 I/O 대기가 없는 부분을 작은 Task로 나누어 스케줄링 한다면 이상적인 스케줄링 전략이 될 것 입니다.
Task들을 위와 같이 작게 나누면 I/O 대기에 시간낭비 하지 않으며 각 연산은 컨텍스트 스위치로 인해 방해받지 않을 수 있는 최적의 스케줄링을 구성할 수 있습니다.
그렇다면 위와 같은 최적의 협력적 스케줄링을 사용하는 코드는 어떻게 작성할 수 있을까요? 다음 포스팅부터는 이러한 스케줄링 전략을 구현하기 위한 여러가지 프로그래밍적 방법들을 이야기해보겠습니다.