티스토리 뷰
본 포스팅은 "프로그래머가 몰랐던 멀티코어 CPU 이야기" 를 읽고 주인장의 생각을 정리한 게시글 입니다.
책정보, 프로그래머가 몰랐던 멀티코어 CPU 이야기 : 네이버 책 (naver.com)
1. 명령어란 무엇인가?
CPU에 대해서 설명할때 자주 등장하는 말이 명령어 입니다.(지난 포스팅의 성능 지표에서도 계속 등장했죠)
컴퓨터에서 작성된 모든 프로그램은 결국 작은 단위의 작업이 순서대로 실행된 결과이고, 이 작업의 정보를 나타내는 것을 명령어라고 할 수 있습니다.
명령어의 예시를 보시죠
명령어는 결국 2진수의 조합으로 되어 있습니다. 이때 32bit cpu는 2진수가 32개 조합된 형태이고, 64bit cpu는 2진수가 64개 조합되어 명령어를 이룹니다.
(즉, 64비트에서는 32비트 명령어를 포함해서 추가적인 명령어를 만드는것이 가능하죠)
여기서 OP Code는 Add, Move, Store와 같은 행동을, Addr1, Add2에서는 메모리 주소를, immediate value는 상수값이 저장됩니다.
모든 명령어가 저런 형식은 아니지만, OP Code + alpha(메모리주소, 상수, Jump후 도착할 메모리 주소...)등으로 구성이 됩니다.
2. 명령어 인출(Instruction Fetch).
이제 명령어가 어떤 것인지는 간단히 알았습니다. 이때 명령어는 "단순히 명령어 실행 -> 끝!" 이 아닙니다.
현 세대 명령어는
명령어 인출 -> 명령어 해독 -> 피연산자 인출 -> 명령어 실행 -> 피연산자 저장
과 같은 순서로, 작업이 분할되어 실행됩니다.
(이 작업은 위처럼 크게보면 5단계고, 세부적으로는 보다 작은 잡업으로 나눠집니다.)
이때 명령어 인출작업에 대해서 간단이 살펴봅니다.
프로그램을 실행할 경우, 해당 프로그램에 필요한 명령어가 메모리에 저장 됩니다.
(컴퓨터를 켜는 작업 또한 하나의 프로그램이며, 해당 작업에 대한 정보는 BIOS(Basic Input Output System)에 저장되어 있는것으로 알려져 있습니다.)
CPU내에는 PC(Program Counter)레지스터가 존재하며, 이 PC가 현재 실행할 명령어가 있는 메모리 주소를 가리킵니다.
이름에서 유추할 수 있듯, Counter 이기 때문에 단순히 숫자를 세는 용도에 지나지 않습니다.
(예를들어 PC가 200번 주소를 가리키고 있다면, 해당 명령어를 실행시킨 다음에는 큰 영향이 없다면 201번 주소를 카리키고 있을겁니다.)
PC
-> MAR(Memory Address Register, PC가 가리키는 주소 저장)
-> MBR(Memory Buffer Register, PC가 가리키던 곳의 명령어 저장)
-> IR(Instrunction Register, 다음에 CPU가 실행할 명령어를 저장)
의 순서로 전기신호가 흐르며, 명령어 인출의 결과로 IR에는 명령어 실행 단계에서 사용될 명령어가 저장되게 됩니다.
위 과정을 간략화해서 C코드로 표현하면 아래와 같게 됩니다.
Void CoreFetch::step()
{
//명령어 TLB를 읽음
core_->memory_->ITLB_.CacheProcess();
//명령어 Cache로부터 Bytes Data를 읽음
core_->memory_->IL1_.CacheProcess();
//위에서 읽은 명령어를 Queue에다가 채움
byteQ_.Request(pc_ & byteQ_line_mask);
//다음 명령어를 가지고올 곳으로 PC위치를 변경
UpdatePC();
core_->memory_->ITLB_.Enqueue(CACHE_READ, pc_, ITLB_Callback, ...);
core_->memory_->IL1_.Enqueue(CACHE_READ, pc_, IL1_Callback, ...);
}
문제는, UpdatePC()는 조상님들이 해주지 않습니다.
명령어 처리 후 PC를 어디로 옮겨야 할지 정해지는 경우가 다수 있기때문에,
이는 나중에 분기예측이라는 큰 과제를 만들어 냅니다.
3. 명령어 해독(Instruction Decode).
명령어는 결국 2진수로 이뤄진 Byte Stream형태입니다.
이를 그나마 인간이 보기 쉽게 16진수 형태로 묶어서표기합니다. 아래는 명령어의 예시입니다.
어쎔블리 : add dword ptr [A], 0x07
실제 명령어(16진수) : 0x83 0x45 0xF8 0x07
실제 명령어(2진수) : 10000011 01000101 11111000 00000111
명령어의 해독이지만, 사실 일반적인 프로그래밍에서 사용되는 Parsing과 유사합니다.
(결국 위 숫자를 보고, 이게 뭘 하는지 기계가 알아들어야 함)
이때 RISC과 CISC의 차이가 발생합니다.
RISC : 명령어의 길이가 정해져 있음 -> 명령어를 해독하기 쉬움(항상 같은 위치에 OPCode, 피연산자 들이 위치)
CISC : 명령어의 길이가 가변적임 -> 명령어를 해독하기 위해 더 많은 H/W가 필요함
때문에 CISC의 명령어 해독 시간을 줄이기 위해서 Partial Decode와 같은 방법등이 적용된다고 합니다.
(일부만 먼저 해독하는 방식으로, Decode에서 Pipeline을 생성)
"RISC과 CISC" 포스팅에서 언급했지만, CISC은 명령어를 MircoOp으로 쪼개는 작업을 합니다.
이때 이 MicroOp으로 쪼개는 작업이 Decoder에서 이뤄집니다.
Micro Op으로 쪼갤 경우, 비동기 처리에도 유리하고 I/O작업 등으로 발생하는 Latency에 보다 유연하게 대응할 수가 있습니다.
예전 그 느렸던 Atom의 경우, Decoder를 간소화 해서 MircoOp으로 쪼개지 않고 CISC을 그대로 실행했다고 합니다.
물론 Atom이 느렸던 이유는 이게 전부는 아닙니다. 나중에 Atom이 느렸던 이유에 대해서 계속 등장합니다.
여기까지 명령어 인출, 해독 과정을 CPU의 FrontEnd라고 불립니다.
(처리할 명령어의 흐름을 만들기 때문이라고 하네요)
'컴퓨터과학 > CPU' 카테고리의 다른 글
명령어 파이프라인(1편) (0) | 2022.06.03 |
---|---|
CPU동작(BackEnd) (0) | 2022.06.02 |
CPU의 성능 지표(CPI, IPC, Clock...) (0) | 2022.05.11 |
가상메모리 (0) | 2022.05.10 |
RISC과 CISC (0) | 2022.05.09 |
- Total
- Today
- Yesterday
- prime number
- GDC
- git
- Greedy알고리즘
- Search알고리즘
- Python
- 알고리즘
- 프로그래머스
- 병렬처리
- heap
- 사칙연산
- 분할정복
- 코딩테스트
- 동적계획법
- 완전탐색 알고리즘
- AVX
- hash
- Sort알고리즘
- stack
- 자료구조
- SIMD
- 컴퓨터그래픽스
- C++
- 이분탐색
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |