CPU는 명령어 하나를 어떻게 실행할까
현대 CPU는 프로그램에 적혀 있는 명령어를 단순히 위에서 아래로 하나씩 실행하지 않는다. 겉으로는 그렇게 보이지만 내부에서는 꽤 많은 일이 동시에 일어난다.
명령어 하나가 실행되기까지
아주 단순하게 보면 CPU는 명령어를 가져오고, 해석하고, 실행한다. 하지만 실제 구현에서는 이 과정이 여러 단계로 쪼개지고 서로 겹쳐서 수행된다.
중요한 건 프로그램이 정의한 결과를 지키는 것이지, 내부에서 반드시 순서대로 계산하는 것은 아니다.
Out-of-order execution
서로 의존하지 않는 명령어라면 CPU는 먼저 실행할 수 있는 작업부터 처리한다. 그러고 나서 최종적으로 프로그램이 기대하는 순서와 결과가 맞도록 정리한다.
int main(void) {
int a = 10;
int b = 20;
int x = a * 4;
int y = b + 7;
return x + y;
}
위 코드에서 a * 4와 b + 7은 서로 직접적인 의존성이 없다.
이런 종류의 작업을 CPU가 병렬적으로 처리할 여지가 생긴다.
결국 왜 이렇게 복잡해졌나
- 메모리는 CPU에 비해 느리다.
- 한 클럭에 가능한 일을 최대한 많이 해야 한다.
- 분기 결과를 기다리는 동안 놀고 있으면 손해다.
- 실행 유닛을 가능한 한 계속 사용하고 싶다.
그래서 캐시, 파이프라인, 분기 예측, speculative execution, reorder buffer 같은 장치가 계속 붙는다.
처음에는 명령어 하나 실행하는 게 별거 아닌 줄 알았는데, 아래 계층으로 갈수록 갑자기 시스템 전체가 거대한 퍼즐처럼 보이기 시작한다.