학습 목표
- 빠른 CPU를 위한 설계 기법을 학습한다.
- 빠른 CPU를 위한 명령어 병렬 처리 기법을 학습한다.
- RISC와 CISC의 차이에 대해 이해한다.
05-1. 빠른 CPU를 위한 설계 기법
클럭
- 컴퓨터 부품들은 ‘클럭 신호’에 맞춰 일사불란하게 움직인다.
- CPU는 ‘명령어 사이클’이라는 정해진 흐름에 맞춰 명령어들을 실행한다.
클럭신호가 빠르게 반복되면 CPU를 비롯한 컴퓨터 부품들은 크만큼 빠른 박자로 움직이기 때문에 클록속도가 높은 CPU는 일반적으로 성능이 좋다.
- 헤르츠(Hz)단위로 측정 (1초 기준)
- 클럭 속도는 일정하지 않다.
- CPU는 base 클럭 속도와 max 클럭 속도로 나눠져있는데, CPU가 고성능을 요하는 순간에는 클럭속도를 높이고, 그렇지 않을때는 낮추기도 한다.
- 최대클럭속도를 강제로 끌어올릴때는 오버클럭킹이라고도 한다.
클럭 속도를 높인다고 해서 CPU가 무조건 빨라지는 것은 아니다. CPU에 무리가 가는 작업(그래픽, 영상 편집 등)을 하게되면 발열문제가 발생하게 된다.
코어와 멀티코어
- CPU코어와 스레드 수를 늘리는 방법도 CPU성능을 높이는 방법 중 하나이다.
- 코어 = CPU 내부에 “명령어를 실행하는 부품”
- CPU = 명령어를 실행하는 부품을 여러개 포함하는 부품
- 멀티코어 CPU 또는 멀티코어 프로세서 = 코어를 여러 개 포함하고 있는 CPU
클럭속도가 2.4GHz인 단일코어 CPU와 클럭 속도가 1.9GHz인 멀티코어 CPU를 비교하면 일반적으로 후자의 성능이 더 좋다.
- CPU 연산 속도가 꼭 코어 수에 비례하여 증가하진 않는다.
- 코어마다 처리할 연산이 적절히 분배되지 않는다면 코어 수에 비례하여 연산 속도가 증가하지 않는다.
- 처리하고자 하는 작업량보다 코어 수가 지나치게 많아도 성능에는 크게 영향이 없다.
코어마다 처리할 명령어들을 얼마나 적절하게 분배하느냐가 중요하고, 그에 따라서 연산 속도는 크게 달라진다.
스레드와 멀티스레드
- 스레드 = 실행 흐름의 단위
- 하드웨어적 스레드
- 하나의 코어가 동시에 처리하는 명령어 단위
- ex. 2코어 4스레드 CPU : 명령어를 실행하는 부품을 두 개 포함하고, 한 번에 네 개의 명령어를 처리할 수 있는 CPU
- 멀티스레드 프로세서 또는 멀티스레드 CPU : 하나의 코어로 여러 명령어를 동시에 처리하는 CPU
- ex. 8코어 16스레드 CPU : 명령어를 실행하는 부품을 여덟 개 포함하고, 한 번에 열여섯 개의 명령어를 처리할 수 있는 CPU를 의미한다. 이는 코어 하나당 두 개의 하드웨어 스레드를 처리한다는 뜻.
- 소프트웨어적 스레드
- 하나의 프로그램에서 독립적으로 실행되는 단위
- 한 프로그램 실행 과정에서 한 부분만 실행될 수 있지만, 프로그램의 여러 부분이 동시에 실행될 수도 있다.
- ex. 아래의 기능을 동시에 수행
- 사용자로부터 입력받은 내용을 화면에 보여 주는 기능
- 사용자가 입력한 내용이 맞춤법에 맞는지 검사하는 기능
- 사용자가 입력한 내용을 수시로 저장하는 기능
- ex. 아래의 기능을 동시에 수행
1 코어 1 스레드 CPU도 여러 소프트웨어적 스레드를 만들 수 있다. 스레드를 실행할 때 엄청나게 빠르게 번갈아가면서 실행되기 때문에 마치 동시에 실행되는 것처럼 사용자 눈에는 보이게 실행해준다.
멀티스레드 프로세서
- 멀티스레드 프로세서의 가장 큰 핵심은 레지스터이다.
- 하나의 명령어를 처리하기 위해 꼭 필요한 레지스터를 여러개 가지고 있으면 된다. = 레지스터 세트
- 프로그램 카운터, 스택 포인터, 메모리 버퍼 레지스터, 메모리 주소 레지스터 등등 (CH04-2)
- 레지스터 세트 개수만큼 여러 명령어를 동시에 실행할 수 있다.
- 프로그램 카운터가 두 개 있다면 “메모리에서 가져올 명령어 주소”를 두 개 지정할 수 있을 것이고, 스택 포인터가 두 개 있다면 두 개의 스택을 관리할 수 있다.
- ALU와 제어장치가 두 개의 레지스터 세트에 저장된 명령어를 해석하고 실행하면 하나의 코어에서는 두 개의 명령어가 동시에 실행된다.
- 논리 프로세서
- 하드웨어 스레드를 이용해 하나의 코어로도 여러 명령어를 동시에 처리할 수 있다.
- 메모리 속 프로그램은 ‘한 번에 하나의 명령어를 처리하는 CPU’나 다름없다. = 몇 코어 몇 스레드인지는 알 수 없다.
- ex. 2코어 4스레드 CPU
- 한번에 4개의 명령어를 처리할 수 있는데 프로그램 입장에서는 한 번에 하나의 명령어를 처리하는 CPU가 4개 있는 것처럼 보인다.
- ex. 4코어 8스레드(논리프로세서) CPU
- CPU 속에 명령어를 처리하는 부품(코어)는 네 개지만, 메모리 속 프로그램이 보기에는 한 번에 하나의 명령어를 처리하는 부품이 마치 여덟 개 있는 것처럼 보이기 때문에 논리 프로세서가 여덟 개로 나오는 것이다.
정리
- 코어 : 명령어를 실행할 수 있는 ‘하드웨어 부품’
- 스레드 : 명령어를 실행하는 단위
- 멀티코어 프로세서 : 명령어를 실행할 수 있는 하드웨어 부품이 CPU안에 두 개 이상 있는 CPU를 의미
- 멀티스레드 프로세서 : 하나의 코어로 여러 개의 명령어를 동시에 실행할 수 있는 CPU를 의미
05-2. 명령어 병렬 처리 기법
명령어 병렬 처리 기법
- 명령어를 동시에 처리하여 CPU를 한시도 쉬지 않고 작동시키는 기법
- 종류
- 명령어 병렬 라이닝
- 슈퍼스칼라
- 비순차적 명령어 처리
명령어 파이프라인
- 명령어 처리 과정을 클럭 단위로 나눠보면
- 명령어 인출(Instruction Fetch)
- 명령어 해석(Instruction Decode)
- 명령어 실행(Execute Instruction)
- 결과 저장(Write Back)
- 같은 단계가 겹치지만 않는다면 CPU는 각 단계를 동시에 실행할 수 있다.
- 명령어 파이프라이닝 : 마치 공장 생산 라인과 같이 명령어들을 명령어 파이프라인에 넣고 동시에 처리하는 기법
파이프라인 위험
- 명령어 파이프라인이 성능 향상에 실패하는 경우
- 종류 : 데이터 위험, 제어 위험, 구조적 위험
1. 데이터 위험
- 명령어 간 ‘데이터 의존성’에 의해 발생한다.
- 모든 명령어를 동시에 처리할 수 없다. 이전 명령어를 끝까지 실행해야만 비로소 실행할 수 있는 경우도 존재한다.
2. 제어 위험
- 프로그램 카운터의 갑작스러운 변화에 의해 발생한다.
- 기본적으로 프로그램 카운터는 ‘현재 실행 중인 명령어의 다음 주소’로 갱신되는데 프로그램 실행 흐름이 바껴 명령어가 실행되면서 프로그램 카운터 값에 갑작스러운 변화가 생긴다면 명령어 파이프라인에 미리 가지고 와서 처리 중이었던 명령어들은 아무 쓸모가 없어지는 경우를 의미한다.
- 분기 예측 = 프로그램이 어디로 분기할지 미리 예측한 후 그 주소를 인출하는 기술
3. 구조적 위험 (= 자원 위험)
- 서로 다른 명령어가 동시에 ALU, 레지스터 등과 같은 CPU 부품을 사용하려고 할 때 발생한다.
슈퍼스칼라
- CPU 내부에 여러 개의 명령어 파이프라인을 포함한 구조
- 슈퍼스칼라 구조로 명령어 처리가 가능한 CPU
- 매 클럭 주기마다 동시에 여러 명령어를 인출할 수도, 실행할 수도 있어야 한다.
- 멀티스레드 프로세서는 한 번에 여러 명령어를 인출하고, 해석하고, 실행할 수 있기 때문에 슈퍼스칼라 구조를 사용할 수 있다.
슈퍼스칼라는 이론적으로는 파이프라인 개수에 비례하여 프로그램 처리 속도가 빨라진다.
But 파이프라인 위험 등의 예상치 못한 문제가 있어 실제로는 반드시 파이프라인 개수에 비례해서 빨라지지는 않는다.
슈퍼스칼라 방식을 차용한 CPU는 파이프라인 위험을 방지하기 위해 고도로 설계되어야한다.
비순차적 명령어 처리 (OoOE, Out ot Order Execution)
- 합법적인 새치기 = 명령어들을 순차적으로 실행하지 않는 기법
- 파이프라인 위험과 같은 예상치 못한 문제들로 인해 파이프라인을 멈추지 않고 진행되게 하는 방식
- ex. 메모리 N번지에 M을 저장하라 = M(N) ←M
위 순서를 비순차적 명령어 처리로 바꾼다면,
비순차적 명령어 처리가 가능한 CPU는 명령어들이 어떤 명령어와 데이터 의존성을 가지고 있는지, 순서를 바꿔 실행할 수 있는 명령어에는 어떤 것들이 있는지 판단할 수 있어야한다.
05-3. CISC와 RISC
명령어 집합
= 명령어 집합 구조 (ISA, Instruction Set Architecture)
- CPU가 이해할 수 있는 명령어들의 모음을 의미한다.
- 명령어의 세세한 생김새, 명령어로 할 수 있는 연산, 주소 지정 방식 등은 CPU마다 조금씩 차이가 있다. = CPU마다 ISA가 다를 수 있다.
- CPU의 언어이자 하드웨어가 소프트웨어를 어떻게 이해할지에 대한 약속이다.
- “구조”라는 단어가 붙은 이유는 CPU가 어떤 명령어를 이해하는지에 따라 컴퓨터 구조 및 설계 방식이 달라지기 때문이다.
- ISA가 다르다는 것은 CPU가 이해할 수 있는 명령어가 다르다는 의미이다. 명령어가 달라지면 어셈블리어도 달라진다.
- 어셈블리어 = 명령어를 읽기 편하게 표현한 언어 (CH03)
- 같은 소스 코드로 만들어진 같은 프로그램이라할지라도 ISA가 다르면 CPU가 이해할 수 있는 명령어, 어셈블리어도 달라진다는 것이다.
- 인텔 노트북 CPU는 x86 혹은 x86-64 ISA를 이해하고, 애플 아이폰 CPU는 ARM ISA를 이해합니다.
- x86(x86-64)과 ARM은 다른 ISA이기 때문에 인텔 CPU를 사용하는 컴퓨터와 아이폰은 서로의 명령어를 이해할 수 없다.
- CPU가 이해하는 명령어(=ISA)들이 달라지면 그에 따른 나비 효과로 많은 것이 달라진다.
- 제어장치가 명령어를 해석하는 방식, 사용되는 레지스터 종류와 개수, 메모리 관리 방법 등
CISC (Complex Instruction Set Computer)
= 복잡한 명령어 집합을 활용하는 컴퓨터 (CPU)
CISC
- 복잡하고 다양한 명령어들을 활용하는 CPU 설계 방식.
- x86, x86-64
- 명령어의 형태와 크기가 다양한 가변 길이 명령어를 활용한다.
- = 상대적으로 적은 수의 명령어로도 프로그램을 실행할 수 있다. (x86-64 코드와 ARM 코드 비교)
- 메모리에 접근하는 주소 지정방식이 다양하다.
프로그램을 실행하는 명령어 수가 적다 = 컴파일된 프로그램의 크기가 작다 같은 소스 코드를 컴파일해도 CPU마다 생성되는 실행 파일의 크기는 다를 수 있다.
장점
- 적은 수의 명령어만으로도 프로그램을 동작시킬 수 있다는 점이 메모리 공간을 절약할 수 있다는 장점이된다.
단점
- 명령어 파이프라이닝에 불리하다.
- 명령어가 워낙 복잡하고 다양한 기능을 제공하는 탓에 명령어의 크기와 실행되기까지의 시간이 일정하지 않다.
- 복잡한 명령어 때문에 명령어 하나를 실행하는 데 여러 클럭 주기를 필요로 한다.
- CICS가 복잡하고 다양한 명령어를 활용할 수 있다고하지만, 대다수의 복잡한 명령어는 사용빈도가 낮고 자주 쓰는 명령어만 쓰게된다.
정리
CISC 명령어 집합은 복잡하고 다양한 기능을 제공하기에 적은 수의 명령으로 프로그램을 동작시키고 메모리를 절약할 수 있지만, 명령어의 규격화가 어려워 파이프라이닝이 어렵다. 그리고 대다수의 복잡한 명령어는 그 사용 빈도가 낮다.
RISC (Reduced Instruction Set Computer)
- 명령어의 종류가 적고 짧고 규격화된 명령어를 사용한다.
- 하나의 명령어가 1클럭 내외로 실행되기 때문에 RISC 명령어 집합은 명령어 파이프라이닝에 최적화되어 있다.
- 메모리 접근을 단순화하고 최소화 추구
- 메모리에 직접 접근하는 명령어를 load, store 두 개로 제한
- load-store 구조라고 부르기도 한다.
- 대신, 레지스터를 적극적으로 활용하기 때문에 CISC보다 레지스터를 이용하는 연산이 많고, 일반적인 경우보다 범용 레지스터 개수도 더 많다.
정리
- ISA : CPU의 언어이자 하드웨어가 소프트웨어를 어떻게 이해할지에 대한 약속
- CISC : 복잡하고 다양한 종류의 가변 길이 명령어 집합을 활용
- RISC : 단순하고 적은 종류의 고정 길이 명령어 집합을 활용