IO-bound 애플리케이션에서 blocking, non-blocking, sync, async 비교

Created
Jan 3, 2021
Created by
Tags
OS
Architecture
Property
 
 

non-blocking과 async의 차이

 
async를 구글링하면 non-blocking이 검색결과에 같이 등장한다. 두 가지 모두 서브루틴을 실행할 동안 메인루틴의 동작을 막지않는 방식이라고 막연히 알고 있었는데 사실 async와 non-blocking은 완전히 독립적인 개념이다. 두 개념을 리눅스에서 실행하는 IO 관점에서 알아본다.
 
blocking & non-blocking
서브루틴에서 IO를 실행할 동안 CPU의 제어가 서브루틴에 있어서 서브루틴이 return할 때 까지 메인루틴이 대기해야 한다면 blocking, 제어가 메인루틴에 있어서 서브루틴의 return을 기다리지 않고 메인루틴의 작업을 진행할 수 있으면 non-blocking이다.
 
sync & async
메인루틴이 서브루틴을 호출했을 때 호출의 결과를 메인루틴이 처리한다면 sync, 다른 루틴(대표적으로 callback function)이 처리한다면 async 이다.
 
 
 

synchronous & blocking IO

메인루틴은 대기하고 메인루틴이 서브루틴 결과를 처리한다.
 
notion image
 
위 그림은 리눅스의 synchronous blocking IO 모델이다. 커널 영역에서 read() 작업을 하는 동안, 즉 서브루틴이 실행되는 동안 애플리케이션 프로세스(메인루틴)는 blocking 된다.
커널로부터 응답이 올 때 까지 CPU가 명령을 실행할 수 없으므로 blocking된 프로세스는 실행(running)에서 봉쇄(wait) 상태로 바뀐다. 그럼 OS는 해당 프로세스의 context를 저장하고 CPU가 명령을 실행할 수 있는 준비(ready)상태의 다른 프로세스를 선택해서 CPU를 할당하는 context switch가 발생한다. read()가 끝나면 프로세스는 unblock 되고 봉쇄(wait) 상태에서 준비(ready)상태로 바뀐다.
 
 

synchronous & non-blocking IO

메인루틴은 대기하지 않으나 메인루틴이 서브루틴 결과를 처리한다.
 
notion image
 
sync & non-blocking 모델은 sync & blocking 에 비해 효율적이지 못하다. 커널의 read()가 애플리케이션 프로세스를 blocking 하지 않지만 애플리케이션 프로세스는 read() 결과를 처리해야 하므로 커널이 read()를 완료했는지 주기적으로 확인해야하기 때문이다. 즉, 애플리케이션 프로세스는 polling을 시도한다.
이 경우 CPU는 바쁜대기(busy-wait) 상태이기 때문에 OS는 다른 프로세스에게 CPU를 할당할 수도 없다. 뿐만 아니라 한번의 polling을 하고 나서 다음 polling을 하기 전까지는 이미 read()가 끝났음에도 커널이 read()를 완료했는지 알 수 없기 때문에 IO에 지연시간(latency)이 늘어난다.
 

asynchronous & blocking I/O

메인루틴은 대기하지만 서브루틴 결과는 콜백이 처리한다.
커널이 read()를 끝냈을 때 select() 라는 콜백을 호출하기 때문에 polling은 하지 않지만 애플리케이션 프로세스는 대기(wait) 상태가 된다. (리눅스의 select() 시스템 콜은 IO에 높은 성능을 요구하는 애플리케이션에는 적합하지 않다고 한다.)
 
notion image
 
 

asynchronous & non-blocking I/O

메인루틴은 대기하지 않고 서브루틴 결과는 콜백이 처리한다.
커널이 read()를 수행하는 동안 제어는 애플리케이션 프로세스로 넘어간다. 따라서 아주 오래 걸리는 IO가 실행되어도 프로세스는 대기하지 않고 CPU를 사용할 수 있고 IO가 먼저 끝나서 버퍼에 들어온 데이터를 처리할 수도 있다.
 
notion image
 
 

결론

IO-bound한 애플리케이션을 구현해야하는 경우
  • sync & non-blocking IO, async & blocking IO 조합은 선택하지 않는 게 좋다.
  • sync & blocking IO 는 위 2가지 조합에 비해 낫지만 짧은 시간의 IO가 자주 있으면 context switching이 빈번하게 발생할 것이다.
  • IO 시간이 짧을 경우 async & non-blocking을 권장하지만 IO시간이 길 경우 async & non-blocking 과 sync & blocking 에 큰 차이는 없을 듯.
 
 
 
출처