데이터베이스에 물어보는 코드 한 줄은, 답을 기다리는 동안 CPU를 얼마나 쓸까요? 금고에 서류를 가지러 간 손님은 창구 자리를 붙잡고 있을 뿐, 그 창구에서 일이 되고 있지는 않습니다.
기초에서 먼저 보기어떤 데이터는 왜 순식간에 오고, 어떤 건 한참 걸릴까?지금까지의 코드는 CPU 안에서만 놀았습니다. 하지만 실제 프로그램은 파일을 읽고, 네트워크로 데이터를 받고, 데이터베이스에 물어봅니다. 이런 작업을 통틀어 IO(입출력)라고 부릅니다. 이 단원의 핵심은 하나입니다 — IO는 CPU 기준으로 어마어마하게 느리고, 그동안 코드는 그냥 멈춰 있습니다.
CPU가 명령 하나를 처리하는 데 대략 1나노초(10억분의 1초)가 걸립니다. 메모리에서 값을 가져오면 약 100나노초, SSD에서 읽으면 약 100마이크로초(10만 나노초), 네트워크 너머 서버에 물어보면 수 밀리초(수백만 나노초)입니다. CPU 시간을 사람 시간으로 확대해 보면, 명령 하나가 1초일 때 SSD 읽기는 하루가 넘고 네트워크 응답은 몇 달에 해당합니다. 이 격차가 이 시리즈 전체의 출발점입니다.
코드가 db.query() 같은 함수를 부르면, 그 함수는 결과가 도착할 때까지 돌아오지 않습니다. 그래서 그다음 줄은 실행되지 못하고 멈춰 있습니다. 이렇게 결과를 받을 때까지 진행이 막히는 것을 블로킹(blocking)이라고 합니다. 애니메이션에서 PC가 한 줄에 붙박여 있고 CPU 표시가 "대기"로 바뀌는 구간이 그것입니다.
4단원을 떠올려 보세요. 참조를 쥐고 있으면 GC는 회수하지 못합니다. 기다리는 중인 함수는 스택 프레임을 그대로 유지하고 있고, 그 안의 변수들이 힙 데이터를 계속 가리키고 있습니다. 즉 기다리는 코드는 CPU는 안 쓰면서 메모리는 쓰는 상태입니다. 이 조합이 나중에 서버에서 어떤 사고를 만드는지 10단원에서 보게 됩니다.
이 애니메이션은 프로그램 하나만 보고 있기 때문에 CPU가 "논다"고 표시됩니다. 하지만 실제 컴퓨터에서 CPU가 정말 놀고 있을까요? 아닙니다 — 운영체제가 그 순간 다른 일감을 CPU에 올립니다. 그 과정이 5·6단원의 주제입니다.
CPU 명령 하나는 나노초 단위입니다. 메모리 접근은 100나노초 안팎, SSD 읽기는 수십 마이크로초, 같은 데이터센터 안의 네트워크 왕복은 0.5밀리초 정도, 다른 지역이면 수십 밀리초입니다. CPU 시간으로 환산하면 디스크 한 번은 명령 수만 개, 원격 호출 한 번은 명령 수천만 개를 실행할 수 있는 시간입니다. 이 격차가 이 시리즈 전체의 출발점입니다.
기다림이 지배적인 작업에서는 계산을 아무리 빠르게 만들어도 전체 시간이 거의 줄지 않습니다. 대신 기다림을 겹치게 만들면 극적으로 줄어듭니다. 요청 열 개를 순서대로 기다리면 열 배가 걸리지만, 동시에 기다리면 한 번에 가까워집니다. 앞으로 나올 스레드풀·이벤트루프·코루틴은 전부 이 겹치기를 어떻게 싸게 만드느냐에 대한 서로 다른 답입니다.
1handle(req) {2 id = parse(req)PC ◀3 user = db.query(id) // DB에 물어보고 기다림4 return toJson(user)5}
서버가 요청을 받으면 handle이 호출됩니다(1단원의 [주소 등록 + 점프] 그대로). 요청에서 id를 꺼냅니다 — CPU가 일하는 중 (0.3ms). PC = 2.
CPU 명령 하나가 1초로 느껴지도록 모든 시간을 10억 배 확대했습니다.
외부 호출 한 번을 기다리는 동안, CPU는 명령 수억 개를 실행할 수 있었습니다.
출처: Jeff Dean · Peter Norvig, “Latency Numbers Every Programmer Should Know” (사람 시간 환산: hellerbarde). 막대는 로그 스케일.
시뮬레이터에서 해보기
이 단원의 용어
왜 이렇게 발전했나
CPU는 반도체 속도로 동작하지만 디스크와 네트워크는 물리적 거리와 기계적 한계에 묶여 있습니다. 이 격차는 앞으로도 사라지지 않습니다. 그래서 컴퓨터 역사에 등장한 실행 모델 대부분은 "기다리는 시간을 어떻게 쓸모 있게 만들 것인가"에 대한 서로 다른 대답입니다.
병목은 계산이 아니라 기다림. 기다리는 코드는 CPU는 안 쓰고 메모리만 쓴다.
남은 문제
기다리는 동안 CPU에게 다른 일을 시키면 되지 않을까요? 그러려면 "다른 일"이 먼저 있어야 합니다 →