Search
📘

그로킹 동시성 [CH 6] - 멀티태스킹

AI custom autofill
Published
2025/05/18
Category
Programming
Tags
BookReview

6.1 CPU 중심과 입출력 중심

애플리케이션은 수치적, 산술적, 논리적 연산을 주로 하므로 CPU 자원을 주료 활용한다.
하지만 키보드, 하드디스크, 프린터 등 다른 장치들과 신호를 주고 받으며 통신하는 형태이기 때문에 CPU 자원을 그리 많이 사용하지 않는다. 이런 연산을 입출력 연산(Input-Output operation, I/O operation)이라고 한다.
병목이 일어나는 자원의 종류에 따라 CPU 중심(CPU-bound)인 것과 입출력 중심(I/O-bound)인 것으로 나눌 수 있다.
작업에 딸린 부하의 유형이 무엇인지 알고 CPU 자원을 할당해야 한다.

6.1.1 CPU 중심

CPU가 빨라지면 성능이 향상하는 애플리케이션
수학적 연산, 암호화/복호화 알고리즘, 이미지 또는 동영상 처리 등

6.1.2 입출력 중심

입출력 하위 시스템의 속도를 개선하면 성능이 향상하는 애플리케이션
디스크 읽기, 사용자 입력받기, 네트워크 응답 시간 등
여러 입출력 연산을 수행하다 보면 CPU가 외부 장치가 데이터를 읽고 쓰기를 마칠 때까지 아무것도 하지 않고 대기하는 시간이 늘어난다. 하지만 CPU의 시간은 매우 비싼 자원이다.
GUI 애플리케이션, 디스크, 데이터베이스, 웹 서버 등

6.1.3 병목 지점 식별하기

애플리케이션의 병목 지점을 판단하려면 먼저 애플리케이션의 성능을 향상시키기 위해 필요한 자원이 무엇인지 알아내야 한다.
CPU 중심: CPU 성능에 의해 속도가 결정된다.
입출력 중심: 입출력 하위 시스템의 성능에 의해 속도가 결정된다.

6.2 멀티태스킹이 필요한 이유

CPU는 점점 더 빨라져 더 많은 인스트럭션을 수행할 수 있게 되었는데, 입출력 중심의 작업은 상대적으로 느리다.
하지만 이들 연산을 식별해 백그라운드로 실행하는 방법이 있으며 대부분의 현대적인 런타임 시스템에서도 이런 방법을 쓴다.
비동기 처리, 이벤트 루프, 멀티태스킹 등
NodeJS, Python asyncio, Javascript의 async/await 등은 입출력 연산을 별도로 처리하거나, 다른 작업을 먼저 하고 나중에 결과를 받아오는 방식

6.3 멀티태스킹: 조감도

오늘날에도 우리는 길을 걸으며 음악을 듣고, 요리하면서 통화를 한다.
멀티태스킹(multitasking)은 일정한 시간 동안 여러 작업을 동시에 실행하여 여러 작업을 수행한다는 개념이다. (막대 위에 올린 접시 여러 개를 돌리는 묘기)
그러나 병렬 실행을 하려면 병렬 실행을 지원하는 하드웨어가 필요하다. 오래된 프로세서로도 겉으로나마 멀티태스킹처럼 보이게 할 수가 있다.

6.3.1 선점형 멀티태스킹

운영체제가 관리하는 자원 중에서 가장 중요한 것은 CPU다. 그렇기에 운영체제는 실행될 모든 프로그램에 CPU를 배정할 수 있어야 한다.
결국 운영체제가 직접 나서 애플리케이션의 실행을 중단시켜야 한다.
대부분의 애플리케이션은 실행 중인 다른 애플리케이션을 배려하지 않는다.
선점형 멀티태스킹(preemtive multitasking)은 한 작업이 실행되는 일정 시간(=타임 슬라이스(time slice))의 정의가 필요하다. 운영체제가 이 조각만큼의 시간을 실행 중인 작업에 배정하도록 보장하기 때문이다. 이때 쓰이는 스케줄링 정책이 타임 셰어링(time sharing) 정책이다.
타임 슬라이스만큼 실행이 끝나면 스케줄러가 작업을 인터럽트(CPU로 하여금 작업을 멈추거나 계속 진행하도록 하는 신호)하고 실행 중이던 작업은 차례를 다시 기다리는 동안 다음 작업을 실행하도록 한다.
인터럽트
하드웨어 인터럽트(키보드 버튼이나 파일 기록 완료)
애플리케이션에서 발생시키는 소프트웨어 인터럽트(시스템 콜)
오류 및 타이머 인터럽트
프로세서가 아주 짧은 시간씩 나누어 현재 실행 중인 작업 사이를 빠르게 전환하며 실행한다면, 각 작업은 시간상으로 엇갈려 실행되는 상황이 된다. 이렇게 큐에 담긴 작업 사이에 제어권을 빠르게 옮겨가는 방식(컨텍스트 스위칭)으로 운영체제는 같은 순간에 실행되는 작업은 하나뿐인데도 마치 모든 것이 동시에 실행되는 듯한 착각을 일으킨다.
근래에 개발된 거의 모든 운영체제는 선점형 멀티태스킹을 사용

6.3.2 선점형 멀티태스킹을 지원하는 게임기

코드에서는, 무한 루프 안에서 CPU 타임 슬라이스를 스레드에 차례로 나눠주는 방식으로 멀티태스킹을 구현
물리적으로는 순차 실행이 일어나고 있지만, 사용자 입장에서는 10ms 정도로 타임 슬라이스를 둔다면, 스레드 간의 전환이 매우 빠를 것이고 세 스레드가 실행하는 작업이 모두 진행 중이므로 마치 동시 실행 중인 것처럼 보인다.
진정한 병렬성은 여러 코어와 같은 하드웨어에 의해 달성되는 낮은 수준의 구현 디테일이지만, 멀티태스킹이라는 운영체제의 기법을 통해 여러 작업이 시간상으로 겹쳐 실행되는 동시성을 추상화하여 제공한다는 것이며 멀티태스킹은 이러한 동시적 컴퓨팅 모델을 가능하게 하는 핵식점인 요소 중 하나이다.

6.3.3 컨텍스트 스위칭

어떤 작업의 실행 컨텍스트에는 현재 실행 중인 코드와 CPU 코어에서 이를 실행하기 위한 모든 것(CPU 플래그 값, 키 레지스터, 변수, 열린 파일, 연결 등)이 포함돼 있다.
컨텍스트 스위칭(context switching)이란 작업의 컨텍스트 정보를 실행했을 때 문제가 없도록 모든 데이터를 챙겨 저장하고 다른 작업의 컨텍스트 정보로 교체하는 물리적 행위이다.
친구랑 대화를 하다, 다른 친구에게 전화를 받고 새로운 컨텍스트로 대화를 이어나가다, 전화를 끊고 기존의 친구와 대화를 하는 형태
컨텍스트 스위칭은 운영체제가 맡으며, 운영체제가 멀티태스킹 기능을 할 수 있는 핵심 매커니즘이다.
컨텍스트 스위칭에도 시스템 자원이 소모되기 때문에 대개는 비용이 큰 행위이다.
먼저 실행 중인 작업의 컨텍스트 정보를 어딘가에 저장해야 하고, 그다음 새로운 작업을 시작한다.
새로운 작업이 완료되면 작업의 마지막 상태를 스케줄러가 저장하고 다음에 실행할 작업의 컨텍스트 정보를 복원한다.
실행 재개
작업 수가 지나치게 많아지면 컨텍스트 스위칭으로 인해 낭비되는 시간이 늘어나 시스템 성능이 저하될 수 있다.

6.4 멀티태스킹 환경

옛날과 달리 오늘날에는 동시에 여러 작업을 수행할 수 있는 능력이 런타임 시스템의 중요한 요구 사항 중 하나가 되었는데, 멀티태스킹이 이러한 능력을 제공
가상 메모리를 통해 컴퓨터가 실제 설치된 것보다 더 많은 양의 메모리를 사용할 수 있게 되었고, 실질적으로 여러 프로그램을 동시에 실행할 수 있게 되었다.

6.4.1 멀티태스킹 운영체제

CPU의 입장에서는 실행 스레드는 오직 하나뿐이며 운영체제에서 주어지는 기계어 인스트럭션을 순차적으로 실행하는 것이다. 이를 위해 운영체제는 스레드와 프로세스를 추상화한다. 싱글 코어 환경에서 여러 스레드가 실행 중이라면 이들 스레드로 재간을 부려 동시에 실행하여 마치 병렬로 실행되는 것처럼 보이게 한다.
멀티태스킹은 런타임 시스템 수준에서 제공되는 기능이다. (하드웨어 수준에서는 멀티태스킹이라는 개념 자체가 없다.) 하지만 그저 되는 것이 아니고, 런타임 시스템에 강력한 작업 격리 기능이나 효율적인 스케줄러가 있어야 한다.

6.4.2 작업 격리

여러 스레드를 갖은 단일 프로세스 혹은 각기 하나 이상의 스레드를 가진 여러 프로세스 방법에는 작업의 실행을 서로 격리한다는 공통점이 있다.
일반적으로 단일 프로세스에서 여러 스레드를 두는 편이 더 효율적이라고 알려져 있다.
스레드의 컨텍스트 스위칭 속도가 프로세스의 컨텍스트 스위칭보다 더 빠르다. 프로세스의 컨텍스트 스위칭의 오버헤드가 더 크다. (프로세스의 컨텍스트 정보 크기가 더 큼)
같은 프로세스에 포함된 스레드는 주소 공간과 프로세스의 전역 변수를 공유할 수 있어 스레드 간 정보 교환 비용을 아낄 수 있다.

6.4.3 작업 스케줄링

스케줄러는 멀티태스킹 운영체제의 핵심 요소이다.
준비 상태에 있는 작업 중에서 다음 차례로 실행할 것을 고르는 것이 역할 (제한된 자원(CPU 시간)을 분배)
CPU는 항상 어떤 작업을 실행 중이여야 하므로 CPU의 시간을 효율적으로 사용해야 한다.
준비 상태에 있는 작업 정보를 단서로 다음에 어떤 작업을 실행해야 하는지 판단하는 것이 스케줄러의 가장 중요한 역할
작업에서 강제로 제어권을 뺏어올 수도, 명시적으로 또는 묵시적으로(작업의 종료) 반납할 때까지 기다릴 수도 있다. 즉, 스케줄러가 다음 실행할 작업이 무엇인지 예측하기가 어렵기 때문에 스케줄러의 특정한 판단에 의존하는 프로그램을 작성해서는 안 된다.
⇒ 가장 중요한 점은 스케줄러로 프로그램을 수정하지 않아도 시스템 성능을 끌어올릴 수 있는 새로운 수단을 얻을 수 있다는 점 (런타임 환경에서 발생하는 오버헤드보다 성능 향상이 더 커야 한다.)