본문 바로가기
카테고리 없음

[ 혼자 공부하는 컴퓨터구조 + 운영체제] Chapter05. CPU 성능 향상 기법

by seoyeonnn 2024. 6. 1.

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

(1) 클럭

클럭 속도가 높아지면 CPU는 명령어 사이클을 더 빠르게 반복하기 때문에, 클럭 속도가 높은 CPU는 빠르게 동작한다.

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

※ 1GHz = 10$^9$Hz

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

 

하지만 클럭 속도를 무작정 높이면 발열 문제가 더 심각해지며, 클럭 속도만으로 CPU의 성능을 올리는 것에는 한계가 있다.

 

(2) 코어와 멀티코어

클럭 속도를 높이는 방법 외에 CPU의 성능을 높이는 대표적인 방법에는 CPU의 코어와 스레드 수를 늘리는 방법이 있다.

 

전통적인 관점에서 '명령어를 실행하는 부품'은 하나만 존재했으나, 오늘날 CPU 내부에는 '명령어를 실행하는 부품'을 얼마든지 만들 수 있게 되었다.

코어(core): CPU 내에서 명령어를 실행하는 부품

 

멀티코어 CPU(= 멀티코어 프로세서): 코어를 여러 개 포함하고 있는 CPU

CPU 종류는 CPU 안에 코어가 몇 개 포함되어 있는지에 따라 싱글코어, 듀얼코어, 트리플코어 등으로 나눌 수 있다.

 

CPU의 연산 속도가 꼭 코어 수에 비례하여 증가하는 것은 아니다.코어마다 처리할 연산이 적절히 분배되지 않거나, 처리하고자 하는 작업량보다 코어 수가 지나치게 많아도 성능에는 크게 영향이 없을 수 있다.

 

(3) 스레드와 멀티스레드

스레드(thread)는 실행 흐름의 단위로, CPU에서 사용되는 하드웨어적 스레드와 프로그램에서 사용되는 소프트웨어적 스레드가 있다.

 

하드웨어적 스레드: 하나의 코어가 동시에 처리하는 명령어 단위

멀티스레드 프로세서(= 멀티스레드 CPU): 하나의 코어로 여러 명령어를 동시에 처리하는 CPU

Ex. 2코어 4스레드 CPU는 명령어를 실행하는 부품을 두 개 포함하고, 한 번에 네 개의 명령어를 처리할 수 있는 CPU를 의미한다.

 

하나의 코어로 여러 명령어를 동시에 처리하도록 하려면 프로그램 카운터, 스택 포인터, 메모리 버퍼 레지스터 등 하나의 명령어를 처리하기 위해 꼭 필요한 레지스터를 여러 개 가지고 있으면 된다.

ALU와 제어장치가 두 개의 레지스터 세트에 저장된 명령어를 해석하고 실행하면 하나의 코어에서 두 개의 명령어가 동시에 실행된다.

 

2코어 4스레드 CPU는 한 번에 네 개의 명령어를 처리할 수 있는데, 프로그램 입장에서 봤을 땐 한 번에 하나의 명령어를 처리하는 CPU가 네 개 있는 것처럼 보인다.

그래서 하드웨어 스레드를 논리 프로세서라고 부르기도 한다.

 

하이퍼스레딩: 인텔의 멀티스레드 기술

 

소프트웨어적 스레드: 하나의 프로그램에서 독립적으로 실행되는 단위

 

하나의 프로그램은 실행되는 과정에서 한 부분만 실행될 수도 있지만, 프로그램의 여러 부분이 동시에 실행될 수도 있다.

Ex. 워드 프로세서 프로그램에서 ① 사용자로부터 입력받은 내용을 화면에 보여 주는 기능 ②사용자가 입력한 내용이 맞춤법에 맞는지 검사하는 기능 ③ 사용자가 입력한 내용을 수시로 저장하는 기능

 

각 기능들을 작동시키는 코드를 각각의 스레드로 만들면 동시에 실행할 수 있다.1코어 1스레드 CPU로도 소프트웨어적 스레드를 여러 개 실행, 즉 프로그램의 여러 부분을 동시에 실행할 수 있다.

 

 

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

명령어 병렬 처리 기법: 명령어를 동시에 처리하여 CPU를 쉬지 않고 작동시키는 기법

대표적인 명령어 병렬 처리 기법에는 명령어 파이프라이닝, 슈퍼스칼라, 비순차적 명령어 처리가 있다.

 

(1) 명령어 파이프라인

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

1. 명령어 인출

2. 명령어 해석

3. 명령어 실행

4. 결과 저장

 

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

명령어 파이프라이닝: 명령어들을 명령어 파이프라인에 넣고 동시에 처리하는 기법

 

파이프라이닝이 높은 성능을 가져오기는 하지만, 특정 상황에서는 성능 향상에 실패하는 경우도 있다.

이러한 상황을 파이프라인 위험이라고 부른다.

파이프라인 위험에는 크게 데이터 위험, 제어 위험, 구조적 위험이 있다.

 

데이터 위험: 명령어 간 '데이터 의존성'에 의해 발생

 

명령어1: R1 ← R2 + R3

명령어2: R4 ← R1 + R5

 

위의 경우 명령어1 실행이 끝나기 전에 명렁어2를 인출하면 R1에 R2 + R3 결괏값이 저장되기 전에 R1 값을 읽어 들이므로 원치 않은 R1 값으로 명령어2를 수행하기 때문에, 명령어2는 명령어1의 데이터에 의존적이다.

데이터 의존적인 두 명령어를 무작정 동시에 실행하려고 하면 파이프라인이 제대로 작동하지 않는다.

 

제어 위험: 주로 분기 등으로 인한 '프로그램 카운터의 갑작스러운 변화'에 의해 발생

 

기본적으로 프로그램 카운터는 '현재 실행 중인 명령어의 다음 주소'로 갱신되지만, 프로그램 실행 흐름이 바뀌어 명령어가 실행되면서 프로그램 카운터 값에 갑작스러운 변화가 생긴다면 명령어 파이프라인에 미리 가지고 와서 처리 중이었던 명령어들은 쓸모가 없어진다.

 

이를 위해 사용하는 기술로 분기 예측이 있는데, 이는 프로그램이 어디로 분기할지 미리 예측한 후 그 주소를 인출하는 기술이다.

 

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

 

(2) 슈퍼스칼라

슈퍼스칼라: CPU 내부에 여러 개의 명령어 파이프라인을 포함한 구조

슈퍼스칼라 프로세서(= 슈퍼스칼라 CPU): 슈퍼스칼라 구조로 명령어 처리가 가능한 CPU

 

이론적으로는 파이프라인 개수에 비례하여 프로그램 처리 속도가 빨라지지만, 파이프라인 위험 등의 문제가 있어 실제로는 반드시 파이프라인 개수에 비례하여 빨라지지는 않는다. 이 때문에 슈퍼스칼라 방식을 차용한 CPU는 파이프라인 위험을 방지하기 위해 고도로 설계되어야 한다.

 

(3) 비순차적 명령어 처리

비순차적 명령어 처리(OoOE; Out-of-order execution): 파이프라인의 중단을 방지하기 위해 명령어들을 순차적으로 실행하지 않는 기법

 

파이프라인 위험과 같은 예상치 못한 문제들로 인해 명령어는 곧바로 처리되지 못하기도 한다. 만약 모든 명령어를 순차적으로만 처리한다면 이러한 상황에서 명령어 파이프라인은 멈춰버리게 된다.

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

 

 

05-3. CISC와 RISC

(1) 명령어 집합

명령어 집합(= 명령어 집합 구조(ISA)): CPU가 이해할 수 있는 명령어들의 모음

CPU마다 ISA가 다를 수 있으며, CPU가 어떤 명령어를 이해하는지에 따라 컴퓨터 구조 및 설계 방식이 달라진다.

Ex. 인텔의 노트북 속 CPU는 x86 혹은 x86-64 ISA를 이해하고, 애플의 아이폰 속 CPU는 ARM ISA를 이해한다.

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

 

ISA는 CPU의 언어이자 하드웨어가 소프트웨어를 어떻게 이해할지에 대한 약속이다.

 

(2) CISC

CISC(Complex Instruction Set Computer): 복잡하고 다양한 명령어들을 활용하는 CPU 설계 방식

Ex. x86, x86-64

 

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

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

※ 프로그램을 실행하는 명령어 수가 적다는 말은 '컴파일된 프로그램의 크기가 작다'는 것을 의미한다.

 

이러한 장점 덕분에 CISC는 메모리를 최대한 아끼며 개발해야 했던 시절에 인기가 높았다.

 

그러나 활용하는 명령어가 워낙 복잡하고 다양한 기능을 제공하는 탓에 명령어의 크기와 실행되기까지의 시간이 일정하지 않으며, 복잡한 명령어 때문에 명령어 하나를 실행하는 데에 여러 클럭 주기를 필요로 한다.

규격화되지 않은 명령어로 인해 파이프라인이 효율적으로 명령어를 처리할 수 없으며, 대다수의 복잡한 명령어는 그 사용 빈도가 낮다.

 

(3) RISC

CISC의 한계가 준 교훈은 다음과 같다.

1. 원활한 파이프아리닝을 위해 명령어 길이와 수행 시간이 짧고 규격화되어 있어야 한다.

2. 복잡한 기능을 지원하는 명령어를 추가하기보다는 자주 쓰이는 기본적인 명령어를 작고 빠르게 만드는 것이 중요하다.

 

RISC(Reduced Instruction Set Computer): 단순하고 적은 수의 고정 길이 명령어 집합을 활용하는 CPU 설계 방식

RISC는 짧고 규격화된 명령어를 활용하기에 명령어 파이프라이닝에 유리하다.

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

 

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

다만, 사용 가능한 명령어 개수가 CISC보다 적기 때문에 더 많은 명령으로 프로그램을 작동시킨다.