테마
CPU 성능 높이기 2
CPU 성능 높이기에서 본 클럭(clock)과 코어(core)에 이어, 세 번째 갈래인 여러 명령어를 겹쳐 처리하는 방법을 다룬다. 명령어 처리 단계를 겹치는 기법과 그 흐름을 막는 걸림돌, 한 박자에 명령어를 여러 개 내보내는 구조를 차례로 본다. 마지막으로 명령어 자체를 어떻게 설계하느냐가 성능에 어떤 영향을 주는지 정리한다.
파이프라이닝: 명령어를 겹쳐 처리하기
CPU는 명령어 하나를 한 번에 처리하지 않고 여러 단계로 나눠 처리한다. 이 단계들은 CPU의 동작에서 본 명령어 사이클(instruction cycle)의 인출·실행을 더 잘게 쪼갠 것이다. 교과서의 대표 예는 다섯 단계다. 명령어 인출(fetch), 해석(decode), 실행(execute), 메모리 접근(memory access), 결과 저장(write back) 순서로 진행한다.
파이프라이닝(pipelining)은 앞 명령어가 두 번째 단계로 넘어가면 바로 다음 명령어를 첫 단계에 넣는 기법이다. 세탁기가 돌 때 건조기도 같이 돌리는 것과 같다. 실제로는 단계마다 부품이 따로 있어서, 한 박자(클럭 신호의 한 주기) 안에 다섯 명령어가 각기 다른 단계를 동시에 지난다.
| 박자 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 명령어 A | 인출 | 해석 | 실행 | 메모리 | 저장 |
| 명령어 B | 인출 | 해석 | 실행 | 메모리 | |
| 명령어 C | 인출 | 해석 | 실행 |
명령어 하나가 끝나는 데 걸리는 시간은 줄지 않는다. 대신 단계가 다 찬 뒤에는 거의 매 박자 명령어 하나씩 끝나므로, 단위 시간에 끝내는 명령어 수인 처리량(throughput)이 늘어난다.
파이프라인 해저드
파이프라이닝을 해도 모든 단계가 늘 꽉 차게 돌지는 않는다. 다음 명령어를 바로 넣을 수 없어 처리 단계가 멈추거나 비워지는 상황을 파이프라인 해저드(pipeline hazard)라 한다. 세 종류가 있다.
- 데이터 해저드(data hazard): 뒤 명령어가 앞 명령어의 결과를 입력으로 써야 하는데 그 결과가 아직 나오지 않은 경우다. 결과가 나오자마자 다음 단계로 바로 넘겨 주는 전달(forwarding) 회로로 대부분 줄인다.
- 제어 해저드(control hazard): 조건에 따라 다음 실행 위치가 갈리는 조건 분기(conditional branch) 명령어의 결과가 나오기 전에는 다음에 어느 명령어를 넣을지 모르는 경우다. 아래에서 볼 분기 예측(branch prediction)으로 대응한다.
- 구조 해저드(structural hazard): 두 명령어가 같은 부품(예: 메모리 접근 통로)을 같은 박자에 쓰려는 경우다. 부품을 늘리거나 한쪽을 기다리게 한다.
분기 예측은 조건 분기가 어느 쪽으로 갈지 과거 기록을 보고 추측해, 그쪽 명령어를 미리 처리 단계에 넣는 기법이다. 맞히면 멈춤 없이 지나가고, 틀리면 미리 처리한 명령어를 전부 버리고 올바른 쪽부터 다시 넣는다. 값이 정렬된 배열(array)을 도는 반복문이 같은 값을 뒤섞은 배열보다 빨리 도는 경우가 있다. 반복문 안에 "값이 기준보다 크면" 같은 조건이 있으면, 정렬된 배열에서는 앞쪽 내내 한쪽으로 가다가 어느 지점부터 계속 반대쪽으로 간다. 분기 방향이 규칙적이라 예측이 잘 맞는다. 뒤섞인 배열에서는 방향이 무작위로 바뀌어 예측이 자주 틀리고, 틀릴 때마다 미리 넣은 명령어를 버린다.
슈퍼스칼라와 비순차 실행
슈퍼스칼라(superscalar)는 이런 처리 단계를 여러 벌 두어 한 박자에 명령어를 여러 개 동시에 내보내는 구조다. 한 박자에 끝나는 명령어가 하나를 넘을 수 있어 처리량이 더 늘지만, 동시에 내보낼 명령어끼리 서로의 결과를 기다리지 않아야 효과가 난다. 오늘날 PC·스마트폰의 CPU 코어는 대부분 슈퍼스칼라다.
비순차 실행(out-of-order execution)은 서로 관계없는 명령어를 프로그램에 적힌 순서와 다르게 먼저 실행하는 기법이다. 앞 명령어가 메모리를 기다리며 멈춰 있어도, 그 결과가 필요 없는 뒤 명령어를 먼저 실행해 부품을 쉬게 하지 않는다. 실행 순서는 바뀌어도 결과를 레지스터(register)와 메모리에 확정하는(프로그램이 볼 수 있는 값으로 반영하는) 순서는 원래 프로그램 순서를 지킨다. 그래서 그 프로그램의 실행 흐름 하나만 놓고 보면 순서대로 실행된 것과 결과가 같다.
ISA와 CISC·RISC
ISA(Instruction Set Architecture, 명령어 집합 구조)는 CPU가 알아듣는 명령어의 종류와 형식에 대한 약속이다. 같은 ISA를 쓰는 CPU는 같은 기계어(machine code)를 실행할 수 있다. 그래서 인텔·AMD의 PC·서버 CPU가 쓰는 x86용으로 만든 실행 파일은, 스마트폰 CPU 대부분이 쓰는 ARM에서 그대로 돌지 않는다.
ISA 설계 방식은 크게 둘로 나뉜다. CISC(Complex Instruction Set Computer, 복잡 명령어 집합 컴퓨터)는 명령어 하나가 많은 일을 하게 해 프로그램 크기를 줄인다. 대신 명령어마다 길이와 처리 시간이 달라 파이프라이닝에 불리하다. RISC(Reduced Instruction Set Computer, 축소 명령어 집합 컴퓨터)는 단순하고 대개 길이가 같은 명령어를 두어 단계를 고르게 나누기 쉽다. RISC는 메모리 접근을 적재(load)·저장(store) 명령어로만 하는데, 이런 설계를 로드-스토어 구조(load-store architecture)라 한다. 느린 메모리 접근이 두 명령어에만 모이므로 나머지 명령어는 레지스터끼리 계산해 처리 시간이 고르고, 처리 단계를 고르게 나누기 쉽다.
| 기준 | CISC | RISC |
|---|---|---|
| 명령어 수·기능 | 많고 복잡하다 | 적고 단순하다 |
| 명령어 길이 | 가변 | 대개 고정 |
| 메모리 접근 | 여러 명령어가 메모리를 직접 다룬다 | 적재·저장 명령어만 메모리에 접근 |
| 대표 | x86 | ARM, RISC-V |
요즘 x86 CPU는 복잡한 명령어를 내부에서 단순한 마이크로 연산(micro-operation)으로 쪼개 처리한다. 그래서 겉의 ISA는 CISC여도 안쪽 실행 부품은 RISC와 비슷하게 동작한다.
면접 포인트
- "파이프라이닝이 빠르게 하는 것은?" → 명령어 하나의 시간이 아니라 처리량. 해저드(데이터·제어·구조)가 걸림돌.
- "분기 예측이 틀리면?" → 미리 넣은 명령어를 버리고 다시 채운다. 예측하기 쉬운 분기가 빠르다.
- "CISC와 RISC?" → 복잡·가변 길이 vs 단순·고정 길이·로드-스토어. x86은 내부에서 마이크로 연산으로 쪼갠다.
