Post

[CS] Chapter 05. CPU 성능 향상 기법

혼자 공부하는 컴퓨터 구조 + 운영체제 (한빛미디어 출판, 강민철 지음) 으로 공부한 내용을 정리합니다.
CS Study를 통해 함께 공부하고 있습니다. [github repository]

✅ 05 - 1. 빠른 CPU를 위한 설계 기법

클럭과 코어, 스레드라는 개념을 학습, 빠른 CPU를 만드는 설계 기법인 멀티코어와 멀티스레드란 무엇인지 이해한다.
핵심 키워드 : 클럭 코어 멀티코어 스레드 멀티스레드

✔️ 클럭

클럭 속도가 높아지면 CPU는 명령어 사이클을 더 빠르게 반복할 것. 따라서 클럭 속도가 높은 CPU는 일반적으로 성능이 좋다. 그래서 클럭 속도는 CPU 속도 단위로 간주되기도 한다.

🔎 클럭 속도
헤르츠(Hz) 단위로 측정한다. 이는 1초에 클럭이 몇 번 반복되는지를 나타낸다.
ex) 100Hz : 클럭이 1초에 100번 반복

클럭 속도는 일정하지 않다
CPU는 일정한 클럭 속도를 유지하기보다는 고성능을 요하는 순간에는 순간적으로 클럭 속도를 높이고, 그렇지 않을 때는 유연하게 클럭 속도를 낮추기도 한다. 최대 클럭 속도를 강제로 더 끌어올릴 수도 있는데, 이런 기법을 오버클럭킹(overclocking)이라고 한다.

클럭 속도를 무작정 높인다고 성능이 좋아지지 않음 -> 발열 문제 때문

✔️ 코어와 멀티코어

CPU의 성능을 높이는 방법으로 코어와 스레드 수를 늘리는 방법이 있다.

🔎 코어(core)
명령어를 실행하는 부품

image

코어를 여러개 포함하고 있는 CPU를 멀티코어(multi-core) CPU 또는 멀티코어 프로세서라고 부른다.

CPU의 연산 속도가 코어 수에 비례하여 증가하지는 않는다.

  • 코어마다 처리할 연산이 적절히 분배되지 않는다면 연산 속도가 증가하지 않는다.
    • ex) 4인 1조 팀플
  • 또한, 처리하고자 하는 작업량에 비해 코어 수가 지나치게 많아도 성능에는 영향이 없다.
    • ex) 4인분의 도시락을 10명의 요리사가 만들기

따라서 중요한 것은 처리할 명령어들을 얼마나 적절하게 분배하느냐이다.

✔️ 스레드와 멀티스레드

🔎 스레드(thread)
사전적인 의미는 ‘실행 흐름의 단위’이다. 스레드에는 CPU에서 사용되는 하드웨어적 스레드가 있고, 프로그램에서 사용되는 소프트웨어적 스레드가 있다.

🔎 하드웨어적 스레드

스레드를 하드웨어적으로 정의하면 하나의 코어가 동시에 처리하는 명령어 단위를 의미한다. 하나의 코어로 여러 명령어를 동시에 처리하는 CPU를 멀티스레드(multithread) 프로세서 또는 멀티스레드 CPU라고 한다.

🔎 하이퍼스레딩(hyper-threading)
인텔의 멀티스레드 기술

🔎 소프트웨어적 스레드

소프트웨어적으로 정의된 스레드는 하나의 프로그램에서 독립적으로 실행되는 단위를 의미한다.

✔️ 멀티스레드 프로세서

어떻게 하나의 코어로 여러 명령어를 동시에 처리할 수 있을까? 가장 큰 핵심은 레지스터이다. 하나의 명령어를 처리하기 위한 레지스터를 여러개 가지고 있으면 된다.

하드웨어 스레드를 논리 프로세서(logical processor)라고 부르기도 한다.

✅ 05 - 2. 명령어 병렬 처리 기법

명령어를 빠르고 효율적으로 처리하기 위해 CPU를 한시도 쉬지 않고 작동시키는 명령어 병렬 처리 기법을 알아보자.
핵심 키워드 : 명령어 파이프라이닝 슈퍼스칼라 비순차적 명령어 처리 기법

명령어를 동시에 처리하여 CPU를 한시도 쉬지 않고 작동시키는 기법인 명령어 병렬 처리 기법(ILP: Instruction-Level Parallelism)을 알아본다. 대표적인 명령어 병렬 처리 기법에는 명령어 파이프라이닝, 슈퍼스칼라, 비순차적 명령어 처리가 있다.

✔️ 명령어 파이프라인

명령어 처리 과정을 클럭 단위로 나누어 보면 일반적으로 다음과 같이 나눌 수 있다.

  1. 명령어 인출(Instruction Fetch)
  2. 명령어 해석(Instruction Decode)
  3. 명령어 실행(Execute Instruction)
  4. 결과 저장(Write Back)

같은 단계가 겹치지만 않는다면 CPU는 ‘각 단계를 동시에 실행할 수 있다’. 예를 들어, CPU는 한 명령어를 ‘인출’하는 동안에 다른 명령어를 ‘실행’할 수 있고, 한 명령어가 ‘실행’되는 동안 연산 결과를 ‘저장’할 수 있다.

image 명령어 파이프라인 시각화

🔎 명령어 파이프라이닝(instruction pipelining)
마치 공장 생산 라인과 같이 명령어들을 명령어 파이프라인(instruction pipeline)에 넣고 동시에 처리하는 기법

파이프라이닝이 높은 성능을 가져오기는 하지만, 특정 상화에서는 성능 향상에 실패하는 경우도 있다. 이러한 상황을 파이프라인 위험(pipeline hazard)라고 부른다. 파이프라인 위험에는 크게 데이터 위험, 제어 위험, 구조적 위험이 있다.

🔎 데이터 위험

데이터 위험(data hazard)은 명령어 간 ‘데이터 의존성’에 의해 발생한다. 어떤 명령어는 이전 명령어를 끝까지 실행해야만 비로소 실행할 수 있는 경우가 있다.

1
2
명령어 1: R1 <- R2 + R3 // R2 레지스터 값과 R3 레지스터 값을 더한 값을 R1 레지스터에 저장
명령어 2: R4 <- R1 + R5 // R1 레지스터 값과 R5 레지스터 값을 더한 값을 R4 레지스터에 저장

예를 들어, 위의 경우 명령어 1을 수행해야만 명령어 2를 수행할 수 있다. 따라서 명령어 2는 명령어 1의 데이터에 의존적이다. 이처럼 데이터 의존적인 두 명령어를 무작정 동시에 실행하려고 하면 파이프라인이 제대로 작동되지 않는 것을 데이터 위험이라고 한다.

🔎 제어 위험

제어 위험(control hazard)은 주로 분기 등으로 인한 ‘프로그램 카운터의 갑작스러운 변화’에 의해 발생한다. 기본적으로 프로그램 카운터는 ‘현재 실행 중인 명령어의 다음 주소’로 갱신된다. 하지만 프로그램 실행 흐름이 바뀌어 명령어가 실행되면서 프로그램 카운터 값에 갑작스러운 변화가 생긴다면 명령어 파이프라인에 미리 가지고 와서 처리 중이었던 명령어들은 아무 쓸모가 없어진다. 이를 ‘제어 위험’이라고 한다.

이를 위해 사용되는 기술 중 하나가 분기 예측(branch prediction)이다. 분기 예측은 프로그램이 어디로 분기할지 미리 예측한 후 그 주소를 인출하는 기술이다.

🔎 구조적 위험

구조적 위험(structural hazard)은 명령어들을 겹쳐 실행하는 과정에서 서로 다른 명령어가 동시에 ALU, 레지스터 등과 같은 CPU 부품을 사용하려고 할 때 발생한다. 구조적 위험은 자원 위험(resource hazard)이라고도 부른다.

✔️ 슈퍼스칼라

슈퍼스칼라(superscalar)는 CPU내부에 여러 개의 명령어 파이프라인을 포함한 구조이다. 슈퍼스칼라 구조로 명령어 처리가 가능한 CPU를 슈퍼스칼라 프로세서 또는 슈퍼스칼라 CPU라고 한다.

✔️ 비순차적 명령어 처리

비순차적 명령어 처리(OoOE: Out-of-order execution)은 오늘날 CPU 성능 향상에 크게 기여한 기법이자 대부분의 CPU가 차용하는 방법이다.

명령어들을 순차적으로 실행하지 않는 기법으로, 명령어의 ‘합법적인 새치기’라고 볼 수 있다.

명령어를 순차적으로만 실행하지 않고 순서를 바꿔 실행해도 무방한 명령어를 먼저 실행하여 명령어 파이프라인이 멈추는 것을 방지하는 기법이다.

✅ 05 - 3. CISC와 RISC

CPU의 언어인 ISA란 무엇인지 이해하고, 현대 CPU의 주요 설게 방식인 CISC와 RISC의 정의와 차이점에 대해 학습한다.
핵심 키워드 : ISA CISC RISC

명령어 파이프라이닝과 슈퍼스칼라 기법을 실제로 CPU에 적용하려면 명령어가 파이프라이닝에 최적화되어 있어야 한다. 이와 관련해 CPU의 언어인 ISA와 각기 다른 성격의 ISA를 기반으로 설계된 CISCRISC를 학습해보자.

✔️ 명령어 집합

CPU가 이해할 수 있는 명령어들의 모음을 명령어 집합(instruction set) 또는 명령어 집합 구조(ISA: Instruction Set Architecture)라고 한다. CPU마다 ISA가 다를 수 있다.

ISA가 다르면 같은 소스 코드로 만들어진 프로그램이라도 CPU가 이해할 수 있는 명령어도 어셈블리어도 달라진다.

✔️ CISC

CISC는 Complex Instruction Set Computer의 약자이다. 그대로 해석하면 복잡한 명령어 집합을 활용하는 컴퓨터를 의미한다. x86, x86-64는 대표적인 CISC 기반의 ISA이다.

CISC는 다양하고 강력한 기능의 명령어 집합을 활용하기 때문에 명령어의 형태와 크기가 다양한 가변 길이 명령어를 활용한다.

다양하고 강력한 명령어를 활용한다는 말은 상대적으로 적은 수의 명령어로도 프로그램을 실행할 수 있다는 것을 의미한다.

프로그램을 실행하는 명령어의 수가 적다는 말은 ‘컴파일된 프로그램의 크기가 작다’는 것을 의미한다. 같은 소스 코드를 컴파일해도 CPU마다 생성되는 실행 파일의 크기가 다를 수 있다는 것이다.

이러한 장점 덕분에 CISC는 메모리를 최대한 아끼며 개발해야 했던 시절에 인기가 높았다. 하지만 CISC에는 치명적인 단점이 존재한다. 활용하는 명령어가 워낙 복잡하고 다양한 기능을 제공하는 탓에 명령어의 크기와 실행되기까지의 시간이 일정하지 않다. 그리고 복잡한 명령어 때문에 명령어 하나를 실행하는 데에 여러 클럭 주기를 필요로 한다.

그리고, CISC가 복잡하고 다양한 명령어를 활용할 수 있다고 하지만, 대다수의 복잡한 명령어는 사용 빈도가 낮다. 명령어 집합 중 20% 정도의 명령어가 사용된 전체 명령어의 80%를 차지한다.

정리하면, CISC 명령어 집합은 복잡하고 다양한 기능을 제공하기에 적은 수의 명령으로 프로그램을 동작시키고 메모리를 절약할 수 있지만, 명령어의 규격화가 어려워 파이프라이닝이 어렵다. 그리고 대다수의 복잡한 명령어는 그 사용 빈도가 낮다. 이러한 이유로 CISC 기반 CPU는 성장에 한계가 있다.

✔️ RISC

  1. 빠른 처리를 위해 명령어 파이프라인을 십분 활용해야 한다. 원활한 파이프라이닝을 위해 '명령어 길이와 수행 시간이 짧고 규격화'되어 있어야 한다.
  2. 어차피 자주 쓰이는 명령어만 줄곧 사용된다. 복잡한 기능을 지원하는 명령어를 추가하기보다는 '자주 쓰이는 기본적인 명령어를 작고 빠르게 만드는 것'이 중요하다.

이런 원칙 하에 등장한 것이 RISC이다. RISC는 Reduced Instruction Set Computer의 약자이다. CISC에 비해 명령어의 종류가 적고, 짧고 규격화된 명령어와 되도록 1클럭 내외로 실행되는 명령어를 지향한다.

RISC는 고정 길이 명령어를 활용한다.

명령어가 규격화되어 있고, 하나의 명령어가 1클럭 내외로 실행되기 떄문에 RISC 명령어 집합은 명령어 파이프라이닝에 최적화되어 있다.

RISC는 메모리에 직접 접근하는 명령어를 load, store 두 개로 제한할 만큼 메모리 접근을 단순화하고 최소화를 추구한다. 이러한 점에서 RISC를 load-store 구조라고 부르기도 한다.

RISC는 메모리 접근을 단순화, 최소화하는 대신 레지스터를 적극적으로 활용한다. 그렇기에 CISC보다 레지스터를 이용하는 연산이 많고, 일반적인 경우보다 범용 레지스터 개수도 더 많다.

🔎 CISC와 RISC 차이 정리

CISCRISC
복잡하고 다양한 명령어단순하고 적은 명령어
가변 길이 명령어고정 길이 명령어
다양한 주소 지정 방식적은 주소 지정 방식
프로그램을 이루는 명령어의 수가 적음프로그램을 이루는 명령어의 수가 많음
여러 클럭에 걸쳐 명령어 수행1클럭 내외로 명령어 수행
파이프라이닝하기 어려움파이프라이닝 하기 쉬움
This post is licensed under CC BY 4.0 by the author.

[CS] Chapter 04. CPU의 작동 원리

[CS] Chapter 06. 메모리와 캐시 메모리