7. IO-Bound & CPU-Bound
📄 1. 사전에 염두해야 하는 용어
1). 작업 요청자와 결과 제공자의 관계
① IO (Input / Output) Bound 작업에서
- 작업 요청자 : 소프트웨어 애플리케이션
- System Call을 IO 장치 호출하는 Side
- System Call 호출은 CPU Bound 이다.
- 결과 제공자 : OS, Hardware, IO Divice
- System Call에 대한 결과를 제공하는 측이고 "IO Completion Port (완료 포트)"를 통해 CPU에 전달한다.
- 사용가능한 결과를 만드는 작업은 DMA나 하드웨어 인터럽트 같은 매커니즘으로 OS나 하드웨어에 의해 IO Bound 작업이 수행된다.
절대로 이 결과를 생성하는데 까지 CPU Bound 작업이 아니다!- Inter Process 애플리케이션이라면 다른 프로세스에서 결과를 생성하는건 CPU Bound가 맞긴한데.
결국 그 결과를 IPC (Inter Process Communication)으로 (파이프든, 소켓)을 사용해서 호출자에게
"데이터 메시지 전송 및 수송 과정" IO Bound 작업이다. 이건 의심할 필요 없이 맞다고 생각해도 된다.
- Inter Process 애플리케이션이라면 다른 프로세스에서 결과를 생성하는건 CPU Bound가 맞긴한데.
- IPC IO Bound 작업
1. Socket 2. FILE 3. Signal 4. Pipe 5. Message Queue
② CPU Bound 작업에서
-
작업 요청자 : 호출자 스레드
Fork()
와Join()
으로 자식 스레드 프로세스 생성하는 Side- Job/Task (CPU 만이 해낼 수 있는 작업)을 여러 CPU 코어에 분할 하는것은 CPU Bound이다.
-
결과 제공자 : 호출자 스레드의 자식 스레드
- 자식 스레드가 호출자를 위해 CPU Bound 작업으로 결과를 생성하고, 제공하는 Side
- 실제로 Job/Task (CPU 만이 해낼 수 있는 작업)을
처리하고, 결과를 생성하고 분비 완료까지 하는 CPU Bound인 것이다.
-
Concurrency & Parrarel CPU Bound 작업
1. 복잡한 수학 계산, 수치 해석 2. 데이터 압축 및 암호화 3. 그래픽 렌더링 이미지 처리 등등.. 4. 컴파일 작업 5. 분할 정복 가능한 알고리즘.
③ 작업이 완료됨이 의미하는 바.
- IO-Bound 에선 리소스 결과물에 반드시 의존이 필요한 타 작업이 갖다 사용가능 하도록
IO 처리자가 결과를 리턴했느냐? - CPU-Bound 에선 좀더 범용적으로
작업,함수의 코드든, 무언가 딱히 리턴안하는 서브루틴이라도 "끝}
" 에 도달했냐?
2). 스레드의 차단
- 이 말은, 스케쥴링의 결과인 Read Queue에 OS에 의해 예약되지 않아서 해당 작업이 큐에 애초에 들어가 있지 않음.
- 주로 OS 호출(커널 레벨 IO) 차원에서 이뤄져, 사용자 모드와 커널 모드이 시스템 콜로 전환된다.
3). Prot
- Connection Port : 두 프로세스 간 연결을 유지하는 포트다.
그리고 그 한쪽 프로세스는 서버에 배포된 것 - IO Port : IO 장치와 메세지를 주고 받고 할 연결을 유지하는 포트다.
- DMA controller
- interrupt controller
- timer
- game controller
- graphics controller
- hard-dist controller
- Communication Port : 두 프로세스간 메시지를 구조 받을때 쓰는 포트다.
📄 2. Blocking & NonBlocking
작업 요청자가 결과를 받아내기 까지 CPU가 의미있는 작업까지 포기할 수 있는지의 마음가짐을 의미한다.
1). Blocking IO
- IO 작업 결과가 사용 가능할 때까지 스레드 블럭 IO 가 완료되기 전까지는 반환되지 않는 요청(request)을 의미한다.
- 송신 프로세스가 메시지가 수신 프로세스 또는 메일박스에 의해 받아들여질 때까지 차단되고,
수신자 또한 메시지가 준비될 때까지 차단되는 통신 모드.
2). NonBlocking IO
- IO 작업 결과는 콜백, 이벤트로 처리
- Receiving Process와 Sending Process간 Communication 할때,
- Sending Process는 (요청 request) 메세지를 수신하는 동시에
- Sending Process는 Receiving Process가 리턴한 메세지의 내용이 유요하든 아니든
(valid message or a null if no message is available)
Sending Process 이후에 처리해야 하는 작업을 멈추지 않고 재개 한다.
3). Blocking IO VS NonBlocking IO
① 공통점
- 둘다 Message에 대해 Sending & Reciving하는 프로세스가 존재하고
두 프로세스간 메세지를 통해 Communication하는 IPC을 다루는 모드라는 것이다. - "자원 요청 -> 결과 반환 -> 결과 사용" 이 동작 만큼은 순서가 보장되는 파이프라인 작업이라는 것이다.
② 차이점
-
"자원 요청 -> 결과 반환" 이 파이프라인에서, 자원을 요청한 스레드의 "사용 가능한 값에 대한 마음가짐", 행동에서 차이가 난다.
-
Blocking IO : 사용 가능한 결과를 받아내기 까지 마음가짐은 (택배를 주문한 순간부터 누가 도둑질 할까봐 두려운거 마냥 온전히 하나의 목표를 IO가 끝났는지 확인하는데 CPU를 전념하는 심정으로 )
"오직 IO 하드웨어의 작업이 완료까지 하염없이 배달을 기다리다 곯아 떨어진다.
그러다 보니 현생을 살 수 없다. ( CPU Bound 작업을 처리하기 위한 CPU 제어권 을 받지 못한다)" -
NonBlocking IO : 사용 가능한 결과를 받아내기 까지 마음가짐은 작업이 완료를 하염없이 기다리지 않고
택배를 주문하기 까지만 CPU가 전념하고 주문 하면 그 이후로는 신경을 아예 끊어버리고 현생을 살러 간다.
( CPU Bound 작업을 처리하기 위한 CPU 제어권 을 돌려 받는다.)
-
-
"결과 반환 -> 결과 사용" 이 파이프라인에서도 당연히 다른 양상을 보인다.
- Blocking IO : 호출자는 사용 가능한 결과물에 대해 보장을 받았으므로 코드 라인 순차적으로 처리한다.
- NonBlocking IO : 호출자는 사용 불가능한 결과물을 받을 수 있으므로
콜백, 이벤트나 폴링을 통해 결과를 처리.
📄 3. Synchronous & Asynchronous
내가 작성한 스크립트 라인이 위에서 아래로 흘러가는지를 의미한다.
- 스트립트 라인이 위에서 아래로 흐른다 : 멀티 스레드 프로그레밍을 애초에 시도 조차 안한 코드를 실행할때 흘러가는 모양새
"그때 스트립트 라인이 위에서 아래로 흐른다" 라고 전제로 깔고 가겠다는 의미고, - 스트립트 라인이 위에서 아래로 흐르지 않는다 : "위에서 아래로 흐르다가" 갑자기 뜬금없이
명시적으로 호출하지도 않은 함수 위치로 코드라인이 점핑할때
1). Synchronous
동기적 스레딩
-
자식 스레드를 생성하는 동작을 하는 스레드가 있다면, 그 동작을 하는 스레드를 부모 스레드라고 명칭 해 보고.
-
부모스레드는 주기가 있다,
- 부모 스레드가 running이다 : 자식 스레드를 만들고 그 스레드에 작업을 할당하여 돌릴때
- 부모 스레드가 terminate다 : 상태는 자식 스레드가 모두 종료될때
-
부모스레드가 하나이상의 자식 스레드를 생성한 후,
자식 스레드들이 종료될 때 까지 기다린 다음에야 부모스레드 자신이 재개되는 스레딩 방식이다.
2). Asynchronous
비 동기적 스레딩
- 자식 스레드를 생성하는 동작을 하는 스레드가 있다면, 그 동작을 하는 스레드를 부모 스레드라고 명칭 해 보고.
- 부모 스레드는 자식스레드의 작업이 수행되는 것과 독립적으로 (자식 스레드들이 끝나든 안끝나든)
부모스레드는 바로 제어권을 가져 계속 작업을 하는 스레딩 방식
3). Synchronous VS Asynchronous
-
- Synchronous
- 복수의 작업이 시작되든, IO System Call 요청후 끝났을때의 코드 라인은 동일한 라인에서 재개하는 반면.
-
- Asynchronus
- 복수의 작업이 시작되든, IO System Call 요청후 끝났을때의 코드 라인은 다소 쌩뚱맞다.
동일한 지점이 아니라 코드 점핑을 해도 된다.
📄 4. 복합적으로 묶어보기
/ | Blocking | NonBlocking |
---|---|---|
Synchronous | "오직 IO 하드웨어의 작업이 완료까지 하염없이 배달을 기다리다 곯아 떨어진다. 그러다 보니 현생을 살 수 없다. ( CPU Bound 작업을 처리하기 위한 CPU 제어권 을 받지 못한다)" 복수의 작업이 시작되든, IO System Call 요청후 끝났을때의 코드 라인은 동일한 라인에서 재개한다 안전 확실 외골수 |
택배를 주문하기 까지만 CPU가 전념하고 주문 하면 그 이후로는 신경을 아예 끊어버리고 현생을 살러 간다. ( CPU Bound 작업을 처리하기 위한 CPU 제어권 을 돌려
받는다.) 복수의 작업이 시작되든, IO System Call 요청후 끝났을때의 코드 라인은 동일한 라인에서 재개한다. 이 말은 무엇이냐? 두가지 케이스로
분류할 수 있는데 사실 완전 웃긴게 1. 현생 살러가기 = 배달이 도착했는지 안했는지 새로고침만 Busy 주구장창하는것 2. 또는, 배달 시켜놓고 현생 살러 떠나고 배달이 왔는데 무심하게 아무것도 안하는거(결과 사용을 안함) ㅋㅋㅋ 왜냐? 그냥 Busy Waiting을 하지 않으면 이 기법에서는 결과를 사용할 방법이 없음 |
Asynchronous | "오직 IO 하드웨어의 작업이 완료까지 하염없이 배달을 기다리다 곯아 떨어진다. 그러다 보니 현생을 살 수 없다. ( CPU Bound 작업을 처리하기 위한 CPU 제어권 을 받지 못한다)" 복수의 작업이 시작되든, IO System Call 요청후 끝났을때의 코드 라인은 다소 쌩뚱맞다. 바로 콜백으로 인해서 동일한 지점이 아니라 코드 점핑을 해도 된다. 이건 그나마 의미가 있는게, WhenAll<TResult>(Task<TResult>[]) |
택배를 주문하기 까지만 CPU가 전념하고 주문 하면 그 이후로는 신경을 아예 끊어버리고 현생을 살러 간다. ( CPU Bound 작업을 처리하기 위한 CPU 제어권 을 돌려 받는다.) 복수의 작업이 시작되든, IO System Call 요청후 끝났을때의 코드 라인은 다소 쌩뚱맞다. 바로 콜백으로 인해서 동일한 지점이 아니라 코드 점핑을 해도 된다. Thread-Per-Core With Non-Blocking CPU효율도 좋고 처리량도 좋다. |
- 사실 Busy Waiting이 의미없는 행동으로 억지로 안전하게 묶은 느낌이 든다면 꼭 그건 아니다.
Busy Waiting이 장점이 있을 때도 있다. 바로- Busy Waiting이 진짜 나노초 단위로 기다리고 해제될 정도로 빠르고
- 오히려 Context-Switcing 오버헤드가 더 클때
- 이럴때가 확실하지 않으면 사실 Non-Blocking Asynchronous 그냥 이거 쓰자.
📄 5. 비동기를 구현하는 메커니즘들
매우 다양한 조합으로 비동기가 구현된다.
① 하드웨어(장치) (DMA)
② OS (논블로킹 IO, 멀티 스레딩)
③ 패턴 (JS 이벤트 루프, 코루틴)
ThreadPool.SetMinThreads(2, 0)
,ThreadPool.SetMaxThreads(4, 1)
를 통해
첫번째 인자는 "WorkerThread Count", 두번째 인자는 "IO Thread Count" 이다.
참조
'CS > OS' 카테고리의 다른 글
| 니앙팽이 - 컴퓨터 구조 | 캐시 Cache (0) | 2025.02.18 |
---|---|
| 니앙팽이 - 멀티스레딩 | 6 | 스레드 (0) | 2025.01.18 |
| 니앙팽이 - 멀티스레딩 | 5 | 성능 평가 척도 (0) | 2025.01.18 |
| 니앙팽이 - 멀티스레딩 | 4 | 프로세스 (0) | 2025.01.18 |
| 니앙팽이 - 멀티스레딩 | 3 | 프로그램을 처리하는 발전 흐름 (1) | 2025.01.18 |