Chapter 05

더 빠른 CPU의 비밀

4장의 장난감 CPU는 명령 하나에 세 박자가 걸렸다. 요즘 CPU는 한 박자에 명령을 여러 개 끝낸다. 어떻게 가능할까? 답은 “더 빨리 일하기”가 아니라 “동시에 여러 일을 겹쳐 하기”와 “미리 짐작해서 하기”에 있다. 세탁소와 요리사의 지혜를 빌려 CPU 속도의 비밀을 하나씩 풀어 보자.

CPU 속도는 세 가지의 곱

CPU가 1초에 처리하는 명령의 수는 대략 다음과 같이 쪼갤 수 있다.

초당 명령 수 = 클럭(초당 박자) × IPC(박자당 명령 수) × 코어 수예: 4 GHz × 4 × 8코어 = 1초에 약 1,280억 개 명령 (이상적인 경우)

박자를 빠르게 하든(클럭), 한 박자에 많이 하든(IPC), 일꾼을 늘리든(코어) 셋 중 하나를 키우면 빨라진다. 지난 50년의 CPU 역사는 이 세 가지를 차례로 키워 온 역사다.

그런데 클럭을 무한정 올릴 수는 없다. 한 박자 안에 신호가 회로의 가장 긴 길을 끝까지 지나가야 하기 때문이다. 3장의 덧셈기에서 올림수가 번져 가는 데 시간이 걸렸던 것을 떠올리자. 그 길이 짧아야 박자도 빨라진다. 그 길을 짧게 자르는 방법이 바로 파이프라인이다.

파이프라인: 세탁소의 지혜

빨래 세 단계

빨래에는 세탁(30분) → 건조(30분) → 개기(30분)가 필요하다. 빨래 네 바구니를 한 바구니씩 끝까지 처리하면 4 × 90분 = 6시간이 걸린다. 하지만 첫 바구니가 건조기에 들어가는 순간 둘째 바구니를 세탁기에 넣으면? 세 기계가 동시에 돌아가서 2시간 30분이면 끝난다. 한 바구니가 걸리는 시간(90분)은 그대로지만, 끝나는 빈도가 30분마다 하나로 빨라진 것이다.

하나씩 차례로4 × 90분 = 6시간 겹쳐서 (파이프라인)2시간 30분 바구니 1바구니 2바구니 3바구니 4 세탁건조개기세탁건조개기세탁건조개기세탁건조개기 306090120150180210240270300330360분 끝 기계 세 대가 동시에 돈다.한 바구니는 여전히 90분이지만30분마다 하나씩 끝난다.
그림 5-1. 파이프라인의 원리. 일을 단계로 나누고 단계마다 다른 기계(회로)를 두면, 앞 일이 끝나기 전에 다음 일을 시작할 수 있다.

CPU도 명령 실행을 여러 단계로 나누고 각 단계를 맡는 회로를 따로 둔다. 전형적인 5단계는 가져오기(IF) → 해석(ID) → 실행(EX) → 메모리 접근(MEM) → 결과 쓰기(WB)이다. 한 명령이 해석 단계로 넘어가면 가져오기 회로는 바로 다음 명령을 가져온다. 이것이 파이프라인(pipeline)이다.

SIMULATOR

5단계 파이프라인

실행 방식
총 걸린 박자—
박자당 명령 (IPC)—
한 명령씩 대비—
가로는 시간(박자), 세로는 명령이다. 해볼 것: 명령을 10개로 늘리면 파이프라인의 이득이 5배에 가까워진다. 의존성을 켜면 3번 명령이 2번의 결과를 기다리느라 빈칸(거품, stall)이 생긴다.

실제 CPU는 파이프라인을 15~20단계로 더 잘게 나누고, 한 박자에 여러 명령을 동시에 가져오는 슈퍼스칼라(superscalar) 구조를 쓴다. 세탁기를 여러 대 두는 셈이다. 게다가 앞 명령을 기다리는 동안 상관없는 뒤쪽 명령을 먼저 실행하는 비순차 실행(out-of-order execution)으로 빈칸을 메운다. 그 덕분에 요즘 CPU의 IPC는 4~8에 이른다.

한 박자에 여러 명령: 슈퍼스칼라와 비순차 실행

파이프라인은 명령 하나하나를 겹쳐 처리하지만, 여전히 한 박자에 한 명령씩 시작한다. 요즘 CPU는 실행 장치(ALU)를 여러 개 두고 한 박자에 명령을 4~8개씩 시작한다. 이것이 슈퍼스칼라다. 요리사에게 손이 여러 개 생긴 셈이다.

그런데 손이 많아도 쓸 일이 없으면 소용없다. 프로그램의 명령은 앞 명령의 결과를 기다리는 경우가 많다. 특히 메모리에서 값을 읽는 명령(LOAD)은 캐시에 있어도 몇 박자가 걸린다. 그 결과를 기다리는 동안 상관없는 뒤쪽 명령을 먼저 처리하는 기술이 비순차 실행이다. 레시피를 위에서부터 그대로 따르는 대신, 물이 끓기를 기다리는 동안 다음 단계의 채소를 미리 써는 요리사와 같다. 결과는 원래 순서대로 정리해서 내놓기 때문에 프로그램 입장에서는 차이가 없다.

SIMULATOR

순서대로 vs 비순차, 손이 몇 개?

실행 방식
총 걸린 박자—
IPC (박자당 명령)—
놀고 있던 칸 비율—
왼쪽은 명령 10개(→ 표시는 앞 명령의 결과가 필요하다는 뜻), 오른쪽 막대는 각 명령이 실행되는 박자다. LOAD는 4박자, MUL은 3박자, 나머지는 1박자가 걸린다고 했다. 해볼 것: “순서대로”에서 손을 4개로 늘려도 별로 빨라지지 않는다. 앞 명령을 기다리느라 막히기 때문이다. “비순차”로 바꾸면 같은 손으로 훨씬 빨리 끝난다.

비순차 실행을 하려면 CPU가 수백 개의 명령을 미리 들여다보고, 누가 누구를 기다리는지 추적하고, 결과를 원래 순서로 되돌리는 복잡한 회로가 필요하다. 요즘 고성능 코어 면적의 상당 부분이 바로 이 일에 쓰인다. 12장에서 볼 GPU가 이런 회로를 빼고 그 자리에 계산 유닛을 채운 것과 정반대의 선택이다.

분기 예측: 갈림길에서 미리 짐작하기

파이프라인에는 고민거리가 하나 있다. 4장의 JZ 같은 조건 점프(분기)를 만나면, 그 조건이 참인지는 실행 단계에 가서야 안다. 그런데 파이프라인은 이미 다음 명령 몇 개를 가져오고 있어야 한다. 어느 쪽 길의 명령을 가져와야 할까?

CPU는 기다리지 않고 짐작한다. 이를 분기 예측(branch prediction)이라 한다. 맞으면 아무 손해 없이 계속 달리고, 틀리면 잘못 가져온 명령들을 모두 버리고(파이프라인 비우기) 다시 시작한다. 한 번 틀릴 때마다 15~20박자를 잃는다.

박자 → 12345678 JZ (갈림길) 여기서야 방향이 확정! 짐작한 명령 ① 짐작한 명령 ② 틀렸다 → 버린다 (파이프라인 비우기) 올바른 명령 잃어버린 박자 실제 CPU는 15~20단계라서한 번 틀리면 15~20박자를 잃는다.
그림 5-2. 분기 예측 실패. 갈림길의 방향은 몇 박자 뒤에야 확정된다. 그동안 짐작으로 가져온 명령이 틀렸다면 모두 버리고 올바른 쪽에서 다시 시작한다.
단골 손님의 주문

매일 아침 8시에 오는 손님이 늘 아메리카노를 시킨다면, 바리스타는 손님이 문을 여는 순간 미리 커피를 내리기 시작한다. 맞으면 손님은 기다리지 않고, 가끔 라떼를 시키면 만든 커피를 버리고 다시 만든다. 손님의 습관이 규칙적일수록 이득이 크다.

SIMULATOR

분기 예측기 대결

분기 패턴 (갈림길에서 실제로 간 방향)
예측 방식
예측 적중률—
틀린 횟수—
잃은 박자 (1회 15박자)—
칸 하나가 분기 한 번이다. 글자는 실제 방향(T = 점프함, N = 안 함), 초록은 예측 성공, 빨강은 실패다. “2비트 카운터”는 두 번 연속 틀려야 생각을 바꾸는 신중한 예측기로, 반복문 끝에서 한 번 탈출해도 쉽게 흔들리지 않는다. 무작위 패턴은 어떤 예측기도 50% 근처를 넘기 어렵다.
정렬하면 빨라지는 이상한 코드

숫자 배열을 돌며 “128보다 크면 더하기”를 하는 코드는 배열을 먼저 정렬하면 몇 배나 빨라진다. 계산량은 같지만, 정렬된 배열에서는 분기가 “안 감, 안 감, …, 감, 감, 감”처럼 규칙적이라 예측이 거의 다 맞기 때문이다. 위 시뮬레이터의 “정렬된 데이터”와 “무작위”를 비교해 보자. 이 사례는 프로그래머 커뮤니티에서 유명한 질문으로, 하드웨어를 이해해야 소프트웨어 성능을 이해할 수 있다는 좋은 예다.

클럭 경쟁의 끝: 전력과 열의 벽

1990년대부터 2000년대 초반까지 CPU 클럭은 해마다 쑥쑥 올라 100 MHz에서 3 GHz를 넘었다. 그런데 2005년 무렵부터 클럭은 3~5 GHz 근처에서 거의 멈췄다. 왜일까?

트랜지스터가 켜지고 꺼질 때마다 전기를 쓰고 열이 난다. 클럭을 올리려면 전압도 올려야 하는데, 소비 전력은 대략 전압의 제곱 × 클럭에 비례한다. 클럭을 1.5배로 올리려고 전압도 높이면 전력은 2~3배로 뛴다. 손톱만 한 칩에서 100 W 넘는 열이 나면 전기 난로의 열판보다 단위 면적당 열이 많아지고, 더 올리면 냉각이 따라가지 못한다. 이를 전력 장벽(power wall)이라 한다.

CHART

데스크톱 CPU의 클럭과 코어 수 추이 (대략)

최고 클럭 (왼쪽 축, 로그 눈금)일반 소비자용 코어 수 (오른쪽 축)
왼쪽 축은 한 칸에 10배인 로그 눈금이다. 1990~2005년 15년 동안 클럭은 약 100배 올랐지만, 그 뒤 20년 동안은 1.5배 정도에 그쳤다. 그 대신 코어 수가 늘기 시작한다. 수치는 대표적인 제품 경향을 단순화한 것이다.

그래서 업계는 방향을 틀었다. 박자를 더 빠르게 하는 대신, 같은 칩 안에 CPU 두뇌(코어)를 여러 개 넣기로 한 것이다. 이것이 멀티코어(multi-core)다. 요리사 한 명을 미친 듯이 빠르게 만드는 대신 요리사를 여러 명 고용하는 셈이다. 스마트폰은 한 걸음 더 나아가, 힘센 코어와 전기를 아끼는 작은 코어를 섞어 두고 일의 종류에 따라 골라 쓴다.

CPU 칩 하나 (다이) 코어 0L1L1L2 코어 1L1L1L2 코어 2L1L1L2 코어 3L1L1L2 L3 캐시 (모든 코어가 함께 씀) 내장 GPU 메모리 컨트롤러 RAM칩 바깥 코어마다 자기L1·L2 캐시가 있고,L3는 함께 쓴다(6장).
그림 5-3. 4코어 CPU 칩의 구조(단순화). 코어는 각각 완전한 CPU 두뇌다. 코어가 많을수록 동시에 여러 프로그램(또는 한 프로그램의 여러 조각)을 실행할 수 있다.

전기와 열: 직접 계산해 보기

칩이 쓰는 전력은 대략 전압² × 클럭 × (켜고 끄는 트랜지스터의 양)에 비례한다. 문제는 클럭을 올리려면 트랜지스터가 더 빨리 반응해야 해서 전압도 같이 올려야 한다는 것이다. 그래서 클럭을 올리면 전력은 클럭보다 훨씬 가파르게, 대략 세제곱에 가깝게 늘어난다. 아래 계산기로 확인해 보자.

SIMULATOR

클럭·전압·코어로 보는 전력 계산기

칩 전체 전력—
성능 (코어 × 클럭)—
전력 1 W당 성능—
비슷한 열을 내는 것—
교육용 단순 모델이다(4 GHz·1.1 V에서 코어 하나 약 12 W로 맞춤). 곡선은 코어 하나의 전력이 클럭에 따라 어떻게 늘어나는지 보여 준다. 해볼 것: 8코어 3 GHz와 4코어 6 GHz를 비교해 보자. 성능 수치는 같은데 전력은 어느 쪽이 적을까? 이것이 멀티코어로 방향을 튼 이유다. 전압이 클럭에 비해 너무 낮으면 회로가 제때 반응하지 못해 계산이 틀린다.

스마트폰의 해법: 큰 코어와 작은 코어

배터리로 움직이는 스마트폰은 한 걸음 더 나아갔다. 빠르지만 전기를 많이 먹는 큰 코어 몇 개와, 느리지만 아주 알뜰한 작은 코어 여러 개를 한 칩에 함께 넣는다. 메시지 알림 확인처럼 가벼운 일은 작은 코어가, 게임이나 사진 보정처럼 무거운 일은 큰 코어가 맡는다. 운영체제의 스케줄러(8장)가 일의 성격을 보고 어느 코어에 줄지 정한다.

스마트폰 칩 큰 코어빠름·전기 많이 큰 코어빠름·전기 많이 작은 코어 4개 GPU · NPU(AI) · 모뎀 · 이미지 처리기 누가 무엇을 맡나 (예) 알림 확인 · 음악 재생 · 백그라운드 동기화작은 코어 웹 페이지 스크롤 · 메신저 입력작은 코어 앱 실행 순간 · 게임 · 사진 보정큰 코어 대부분의 시간은 작은 코어만 깨어 있어배터리가 하루를 버틴다.
그림 5-4. 큰 코어와 작은 코어를 섞은 구성(Arm의 big.LITTLE 등). 최신 PC용 CPU도 같은 방식을 쓰기 시작했다.
예측해 보기

클럭을 2배로 올리면서 필요한 만큼 전압도 올렸다. 전력은 대략 몇 배가 될까?

먼저 골라 보고, 위 계산기에서 확인해 보자.

클럭이 2배면 전력도 2배, 여기에 전압까지 올리면 전압의 제곱만큼 또 늘어난다. 계산기에서 3 GHz → 6 GHz로 바꿔 보면(전압 자동) 코어 하나의 전력이 몇 배로 뛰는지 볼 수 있다. 같은 전력으로 코어 수를 늘리는 편이 훨씬 이득인 이유다.

요리사를 늘리면 무조건 빨라질까: 암달의 법칙

요리사가 8명이면 요리가 8배 빨리 나올까? 감자 깎기, 채소 썰기는 나눠서 할 수 있다. 하지만 “국물을 3시간 끓이기”는 요리사가 몇 명이든 3시간이 걸린다. 프로그램도 마찬가지로, 나눠서 동시에 할 수 있는 부분(병렬 부분)과 반드시 순서대로 해야 하는 부분(순차 부분)이 섞여 있다.

암달의 법칙(Amdahl's law)은 이렇게 말한다. 순차 부분이 전체의 10%라면, 코어를 아무리 늘려도 10배 이상 빨라질 수 없다. 그 10%는 늘 그대로 남기 때문이다.

SIMULATOR

코어를 늘리면 얼마나 빨라질까

빨라진 배수—
이론상 최대—
코어 효율—
위 막대는 작업 시간(회색 = 순차 부분, 파랑 = 병렬 부분), 아래 곡선은 코어 수에 따른 속도 향상이다. 해볼 것: 병렬 부분이 99%여도 코어 256개의 효율은 얼마나 될까? 병렬 비율을 50%로 낮추면 코어를 늘리는 것이 거의 의미가 없어진다.

그래서 게임이나 사무용 프로그램처럼 순차 부분이 큰 일은 코어 수보다 코어 하나의 성능(클럭 × IPC)이 중요하고, 영상 편집이나 과학 계산처럼 잘게 나눌 수 있는 일은 코어 수가 중요하다. 극단적으로 잘 나뉘는 일, 예를 들어 화면의 수백만 픽셀을 각각 계산하는 일을 위해 코어를 수천 개 넣은 것이 GPU다(12장).

핵심 정리

  1. CPU 성능 ≈ 클럭 × IPC(박자당 명령 수) × 코어 수.
  2. 파이프라인은 명령 실행을 단계로 나눠 겹쳐 처리한다. 한 명령의 시간은 그대로지만 처리량이 늘어난다.
  3. 앞 명령의 결과가 필요한 의존성이 있으면 파이프라인에 빈칸(stall)이 생긴다. 비순차 실행이 이를 줄인다.
  4. 조건 점프의 방향을 미리 짐작하는 분기 예측이 틀리면 파이프라인을 비워야 해서 많은 박자를 잃는다. 규칙적인 패턴일수록 잘 맞는다.
  5. 전력과 발열 때문에 2005년 무렵 클럭 상승이 멈추고 멀티코어 시대가 왔다.
  6. 슈퍼스칼라는 한 박자에 여러 명령을 시작하고, 비순차 실행은 기다리는 명령 대신 준비된 명령을 먼저 처리해 IPC를 높인다.
  7. 전력은 대략 전압² × 클럭에 비례한다. 스마트폰은 큰 코어와 작은 코어를 섞어 배터리를 아낀다.
  8. 암달의 법칙: 순차 부분이 남아 있는 한 코어를 아무리 늘려도 속도 향상에는 한계가 있다.

확인 퀴즈

파이프라인을 쓰면 무엇이 좋아지는가?

세탁소 비유처럼 한 바구니의 시간은 같지만, 여러 바구니가 겹쳐 돌아가서 끝나는 빈도가 높아진다.

분기 예측이 틀렸을 때 CPU가 하는 일은?

미리 해 둔 일을 버리고 다시 시작한다. 결과는 항상 정확하지만, 그만큼 시간을 잃는다.

2005년 무렵부터 CPU 클럭이 크게 오르지 않은 주된 이유는?

전력은 대략 전압² × 클럭에 비례한다. 열을 식힐 수 없는 지점(전력 장벽)에 도달해 멀티코어로 방향을 바꿨다.

프로그램의 80%만 병렬로 나눌 수 있다. 코어를 무한히 늘리면 최대 몇 배까지 빨라질까?

순차 부분 20%는 줄일 수 없으므로 최소 시간은 원래의 0.2배. 1 ÷ 0.2 = 5배가 한계다.

비순차 실행이 IPC를 높이는 방법은?

실행은 순서를 바꾸지만 결과는 원래 순서대로 정리한다. 그래서 프로그램은 차이를 느끼지 못한다.

스마트폰 칩이 큰 코어와 작은 코어를 함께 두는 이유는?

대부분의 시간은 가벼운 일이다. 그때는 작은 코어만 깨워 두고, 무거운 일이 생길 때만 큰 코어를 쓴다.