CPU/구조와 원리

< CPU
  • 상위 문서 : CPU

1 명령 주기

파일:Attachment/CPU/basic-instruction-processing-cycle.jpg
간단화 한 CPU Instruction Cycle(명령 주기)
파일:Attachment/CPU/basic-instruction-processing-cycle-with-exseption-hangling.jpg
예외처리를 추가한 간단한 CPU Instruction Cycle(명령주기)

기본 구성으로는 레지스터[1], 프로그램카운터[2], 명령어 레지스터 [3], 산술논리연산장치(ALU: arithmetic logic unit)[4], 제어부[5]와 내부 버스 등이 있다. 그 외에도 캐시 같은 부가 장치도 들어가 있는 경우가 대다수.

2 설명

거의 모든 종류의 CPU가 하는 일은 요약해보면 대부분 고작 아래 4기능이 전부다.

  • Fetch(인출) : 메모리상의 프로그램 카운터가 가리키는 명령어를 CPU로 인출하여 적재.
  • Decode(해석) : 명령어의 해석. 이 단계에서 명령어의 종류와 타겟 등을 판단한다.
  • Execute(실행) : 해석된 명령어에 따라 데이터에 대한 연산을 수행한다.
  • Writeback(쓰기) : 명령어대로 처리 완료된 데이터를 메모리에 기록한다.

가장 어려운 부분을 스킵해서 쉬워보이는 건 기분탓

위의 CPU는 아주 초급적인 교과서적인 설명에 기반하며, 어떤 상용 CPU도 저런 구조를 가지지 않는다. 그나마 명령어의 실행 순서를 섞을 수 없는 Inorder CPU[6]를 엄청나게 단순화하면 저런 설명이 가능하다고 우길 수도 있으려나? 여튼, 명령어의 실행 순서를 섞을 수 있는 현대의 Out-of-order CPU 들은 아래와 같은 순서를 거쳐 명령어를 실행한다.

  • Fetch: 실행할 명령어들을 가져온다. 이후 단계들도 그렇지만, 한 번에 보통 4개 정도를 처리하는데, Superscalar라고 불리는 기술은 이렇게 한 사이클에 여러 명령어를 처리하는 것을 말한다.
  • Decode: 이후 처리를 돕기 위해 명령어의 종류를 구분한다. Intel의 x86 ISA같이 복잡한 명령어를 쓰는 CISC의 경우, 내부적으로 RISC 명령어들로 쪼개지는 과정[7]이 여기서 수행된다. 옛날 프로그램을 짜기 어려웠던 이유는 바로 이 디코드 동작시 불러오는 CPU 명령함수들이 CPU마다 전부 다 다르다는 것 때문이었다. 현재는 몇 개 회사가 CPU 공급을 독점하면서 그나마 단순화되었다. (같은 회사는 추가 기능이 없다면 같은 명령함수를 쓰는 게 일반적)
  • Rename: 명령어가 가리키는 레지스터(CPU에서 값을 저장하는 x86 ISA의 eax나 ebx등의 이름있는 공간들)를, 내부에 숨어있는 물리적 레지스터로 매핑한다. 이러한 과정은 Out-of-order CPU에서 발생하는 False-dependency[8] 문제를 해결하기 위해 필수적이다.
  • Dispatch: 명령어가 실행하기 위해 기다리는 대기열 (ROB[9], IQ[10], LSQ[11])에 명령어를 넣는다.
  • Issue: 대기열에 있는 명령어가 실행될 수 있으면[12] 실행하기 위한 장치(가령 계산 명령어는 ALU, 메모리 명령어는 Cache)로 보낸다. 참고로 프로그램에서 시간상 뒤의 명령어가 앞의 명령어보다 먼저 Issue될 수 있다. 이것이 바로 Out-of-order CPU의 핵심 동작 중 하나다.
  • Execute: 실행한다 더 설명이 필요한지?
  • Writeback: 결과값을 레지스터에 써야 한다면 쓴다. 결과값을 기다리고 있던 명령어가 있다면 결과가 생겼다고 알려준다.
  • Commit: 명령어 수행을 완료하고, 명령어 실행을 위해 할당받은 자원을 모두 토해낸다. 명령어의 실행 결과를 사용자에게 노출시키며 (이거 전에는 노출이 안 된다), 이후로는 명령어의 실행을 취소[13] 할 수 없다.

그리고 Out-of-order CPU를 만들기 위해선, 위의 명령어 처리 과정 외에도 몇몇 핵심 기술들이 요구된다.

  • Cache (캐시 메모리): 주 메모리 값을 임시 저장하는 작고 빠른 메모리. 주 메모리 접근은 CPU입장에서 100사이클 정도 놀게 만드는 엄청나게 느린 동작이므로 Cache가 없으면 명령어를 빠르게 실행하고 싶어도 값이 준비가 안 돼서 불가능하다. 캐시 메모리도 한 개만 존재하는 것이 아니라, 보통 중요도와 접근 빈도에 따라 L1~L3의 3개 캐시 메모리를 사용하며, 숫자가 작을수록 용량이 더 작고, 더 빠르다. 또한 캐시 메모리만을 위한 색인 제조 방법과 구동방식은 해당 문서에서 확인 바람.
  • Branch prediction: Fetch를 매 사이클마다 하기 위해선 다음에 실행할 명령어 주소를 알아야 하는데, branch 명령의 결과가 나오려면 꽤나 시간이 걸린다 (게다가 프로그램마다 차이는 있지만, 명령어의 약 30%정도는 branch이다.) 그러니 branch 명령의 결과를 과거의 기록을 기반으로 예측하는 기술이 필요하다.
  • Speculative memory disambiguation: 메모리 접근 명령어들은 메모리 접근 전에 접근할 메모리 주소를 먼저 구해야 한다. 이 때문에 만약 LSQ에 Store 명령이 접근할 메모리 주소를 계산하기 위해 대기중이면, 이후 모든 Load 명령어는 접근할 메모리 주소가 준비되었다 하더라도 메모리 접근을 할 수 없다. 혹시라도 그 Store가 접근하는 주소가 Load가 접근하는 주소랑 같으면 가장 신선한 값은 메모리가 아니라 그 Store 명령어가 가지고 있기 때문이다.[14] 하지만 할 수 없는 걸 그냥 해버리면? (즉, Speculative memory disambiguation) 역시 Out-of-order CPU! Inorder CPU가 못하는 걸 태연하게 해버려! 거기에 동경하게 돼 대부분의 경우 접근하는 메모리 주소는 다르기 때문에 문제가 없으며, 문제가 있더라도 그 Store가 접근하는 메모리 주소를 계산하기 전까진 Load들을 Commit을 하지 않는 것으로 실행 취소가 가능하다.
그런데 위의 설명도 멀티코어 프로세서로 가면 CPU의 일부밖에 설명하지 못한다. 고만 해 미친 놈들아! 설명조차 미친 짓이라면 그걸 만드는 더 미친 놈들이다!! 공돌이를 추모합시다.
  1. 이동 Register(REG or R) : 연산유닛과 연결된 액세스속도가 가장 빠른 기억장치
  2. 이동 Program Counter(PC) : 다음에 인출할명령어의 주소를 가지고 있는 레지스터, 각 명령어가 인출된 후에는 명령어 길이만큼 주소를 증가시킴으로써 주소를 포인팅함. 분기(jump) 명령어가 실행되는 경우에는 목적지주소로 갱신한다
  3. 이동 Instruction Register(IR) : 현재 실행중인 명령의 내용을 기억하고 있는 레지스터
  4. 이동 각종 산술연산(덧셈, 뺄셈, 곱셈, 나눗셈)과 논리연산(AND, OR, NOT, XOR 등)들을 수행하는 회로들로 이루어진 회로
  5. 이동 Control Unit : 명령어를 해석하고, 그것을 실행하기 위한 제어 신호들(control signals)을 순차적으로 발생하는 회로
  6. 이동 많은 저전력 CPU가 이러한 구조이다. 가령 예전의 Atom CPU가 그러하다.
  7. 이동 가령 movl %eax,16(%esp) 같은 더러운 명령어는 레지스터 읽기, 쓰기, 메모리 접근의 세 가지 동작으로 쪼개질 것이다.
  8. 이동 True dependency인 read-after-write와 다르게, 실행 순서를 섞는 바람에 생긴 원래는 발생할리 없던 문제들이다. 구체적으로 write-after-read나 write-after-write가 있으며, 가령 전자는 앞선 명령어가 읽을 값을 뒤따르는 명령어가 쓰기 동작으로 덮어쓰는 상황을 일컫는다.
  9. 이동 ReOrder Buffer. 명령어들의 프로그램에서의 원래 순서를 기억하는 장소.
  10. 이동 Issue Queue. 명령어가 실행될 수 있을 때까지 기다리는 장소.
  11. 이동 Load-Store Queue. 메모리 명령들이 처리되는동안 들어가있는 장소. 꽤나 복잡하기 때문에 자세한 설명은 아래 Speculative memory disambiguation에서 설명.
  12. 이동 다음 사이클에 내가 필요한 값이 준비되며, 실행을 위한 장치를 쓸 수 있을 때
  13. 이동 branch prediction등의 틀릴 수 있는 동작은 틀렸을 때 취소할 수 있어야 한다.
  14. 이동 접근할 메모리 주소가 나올 때까지 대기하고, 또 주소들을 비교하는 일련의 동작들을 위해 LSQ가 필요한 것이다. LSQ는 이게 어떤 것이라고 정의를 설명하는 것보다, 용례로 설명하는 것이 편하다.