CPU Bound 프로세스는 I/O보다 연산으로 이루어진 CPU Burst가 큰 프로세스를 의미한다. I/O Bound 프로세스는 I/O가 많은 작업, 즉 I/O Burst가 큰 프로세스를 의미한다. (게임과 같이 입력장치와 통신이 많은 상황)
실제 프로그램은 CPU와 I/O가 번갈아가면서 작업이 이루어지게 되는데, 다시 한번더 정리하면 아래와 같다.
CPU Burst : CPU 명령을 실행하는 시간
I/O Burst : I/O를 요청한 뒤 기다리는 시간
CPU Bound는 프로세스가 진행될 때, CPU 사용 시간이 I/O Wating보다 많은 경우다. 주로 행렬 곱이나 고속 연산을 할 때 나타나며 CPU 성능에 의해 작업 속도가 결정된다.
반면 I/O Bound는 프로세스가 진행될 때, I/O Wating 시간이 많은 경우다.
파일 쓰기, 디스크 작업, 네트워크 통신을 할 때 주로 나타나며 작업에 의한 병목 (다른 시스템과 통신을 할 때 나타남)에 의해 작업 속도가 결정된다.
CPU의 성능이 향상되거나 개수가 추가되면 CPU Bound의 작업 처리 성능이 향상된다. 따라서 CPU Burst를 해결하기 위해서는 성능 향상을 위해 Scale-up이 주로 사용된다.
반면, I/O Bound의 경우에는 CPU 성능보다 타 시스템과의 병목 부분(I/O Wating)에 큰 영향을 받기 때문에 스레드 개수를 늘리거나 동시성을 활용한다. 따라서 성능 향상을 위해 Scale-out을 주로 사용한다.
Multiprocessing 방식은 Multiple Processes를 사용하며 고가용성(CPU) Utilization와 같은 CPU-Bound Application 처리에 적합하다.
10개의 부엌(Processes), 10명의 요리사(Threads), 10개의 요리(Work)
Multithreading 방식은 Single(Multi) Process, Multiple Threads를 사용하며 I/O Bound 중에서 빠르게 처리해야 하는 Application에 적합하다.
1개의 부엌(Process), 10명의 요리사(Threads), 10개의 요리(Work)
Async IO는 Single Process, Single Thread를 활용하며 I/O Bound 중 천천히 처리해도 괜찮은 Application에 적합하다.
1개의 부엌(Process), 1명의 요리사(Thread), 10개의 요리(Work)
파이썬은 메모리 관리 방법으로 레퍼런스 카운팅(Reference Counting)을 사용한다. 이는 파이썬 오브젝트마다 레퍼런스로 사용된 횟수를 저장하는 것이고, 0이 되는 경우 메모리에서 해제하는 방식이다.
이러한 레퍼런스 카운트의 문제점은 여러 쓰레드가 동시에 객체의 참조 카운트(refcount)를 수정할 수 있으니 경쟁조건(Race Condition)이 발생한다. 그래서 이 문제를 막으려면 refcount를 수정하려고 할 때마다 lock을 걸어야 하는데 모든 객체에 lock을 걸어야 하는 것도 부담이고, 교착상태(Deadlock)의 위험성도 있다.
따라서 이 문제를 해결하고자 단 하나의 Lock, 인터프리터를 통째로 Lock을 거는 방식으로 GIL(Global Interpreter Lock) 이라고 한다. 이 방식은 Deadlock을 방지할 수 있고 모든 변수에 Lock을 거는 것으로 인한 성능 저하는 없어지지만, 모든 CPU 위에서 올라가는 프로그램을 Single-Threaded로 만든다는 단점이 있다.
I/O Bound의 경우에는 GIL에 의한 성능 영향이 없지만, CPU Bound 프로세스인 경우에는 멀티쓰레드로 만든다고 해도 단일쓰레드로 작동하는 문제가 발생한다. 이 문제를 해결하기 위해서는 Multithreading 보다는 Multiprocessing 방식을 채택하는 방법이 있는데 프로세스는 독립적인 메모리 할당이 필요하고 자체적 오버헤드가 있어서 순수한 멀티쓰레드보다는 성능이 떨어질 수 있다.