Computer Science/컴퓨터 구조

CPU 성능 향상 기법

물리 water lee 2024. 11. 8. 18:56

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

📌 클럭

컴퓨터 부품들은 '클럭 신호'에 맞춰 일사불란하게 움직입니다.

그리고 CPU는 '명령어 사이클'이라는 정해진 흐름에 맞춰 명령어들을 실행합니다.

 

클럭 신호가 빠르게 반복된다면

CPU를 비롯한 컴퓨터 부품들은 그만큼 빠른 박자에 맞춰 움직이게 됩니다.

 

즉, 클럭 속도가 높아지면 CPU는 명령어 사이클을 더 빠르게 반복할 것이고, 다른 부품들도 그에 발맞춰 더 빠르게 작동할 것입니다.

 

실제로 클럭 속도가 높은 CPU는 일반적으로 성능이 좋습니다.

 

그래서 클럭 속도는 CPU 속도 단위로 간주되기도 합니다.

 

클럭 속도는 헤르츠(Hz) 단위로 측정합니다.

이는 1초에 클럭이 몇 번 반복되는지를 나타냅니다.

가령 클럭이 똑-딱-하고 1초에 한 번 반복되면 CPU 클럭 속도1Hz인 것이고,

클럭이 1초에 100번 반복되면 CPU 클럭 속도100Hz인 셈이지요.

 

실제 CPU 클럭 속도는 어떤지 볼까요?

사진 속 CPU를 보면 기본 속도(Base)는 2.5GHz, 최대 속도 4.9GHz라는 것을 알 수 있습니다.

이는 1초에 클럭이 기본적으로 25억(2.5*10^9)번, 순간적으로 최대 49억(4.9*10^9)번 반복된다는 것을 나타냅니다.

 

더보기

📌 클럭 속도는 일정하지 않습니다.

'클럭'이라는 단어만 보고 시계를 떠올려 클럭 속도가 매번 일정하게 유지된다고 생각할 수도 있지만,

실제로는 그렇지 않습니다.

 

CPU 사진을 다시 보면 기본 클럭 속도(Base)와 최대 클럭 속도(Max)로 나뉘어 있죠?

 

이처럼 CPU는 계속 일정한 클럭 속도를 유지하기보다는 고성능을 요하는 순간에는 순간적으로 클럭 속도를 높이고, 그렇지 않을 때는 유연하게 클럭 속도를 낮추기도 합니다.

최대클럭 속도를 강제로 더 끌어올릴 수도 있는데, 이런 기법을 오버클럭킹(Overclocking)이라고 합니다.

 

그럼 클럭 속도를 무지막지하게 높이면 무조건 CPU가 빨라질까요?

 

정답은 ... ❌

그래픽이 많이 요구되는 게임이나 영상 편집과 같이 CPU에 무리가 가는 작업을 장시간 하면 컴퓨터가 뜨겁게 달아오르는 것을 경험해 본 적이 있을 것입니다.

클럭 속도를 무작정 높이면 이러한 발열 문제가 더 심각해집니다.

이처럼 클럭 속도를 높이는 것은 분명 CPU를 빠르게 만들지만, 클럭 속도만으로 CPU의 성능을 올리는 것에는 한계가 있습니다.

 

📌 코어와 멀티코어

클럭 속도를 높이는 방법 외에 CPU의 성능을 높이는 방법에는 어떤 것들이 있을까요?

대표적인 해결책으로는 CPU의 코어와 스레드 수를 늘리는 방법이 있습니다.

 

먼저 코어를 늘리는 방법에 대해서 알아봅시다.

 

우선 용어 정리가 필요합니다.

  • CPU를 '명령어를 실행하는 부품'이라고 소개했습니다.
    • 원칙적으로 '명령어를 실행하는 부품'은 하나만 존재했으나 오늘날 CPU는 많은 기술적 발전을 거듭하였고, 그 결과 CPU 내부에는 '명령어를 실행하는 부품'을 얼마든지 만들 수 있게 되었습니다.
  • 우리가 지금까지 CPU의 정의로 알고 있었던 '명령어를 실행하는 부품'은 오늘날 코어(core)라는 용어로 사용됩니다.
    • 다시 말해, 오늘날의 CPU는 단순히 '명령어를 실행하는 부품'에서 '명령어를 실행하는 부품을 여러 개 포함하는 부품'으로 명칭의 범위가 확장되었습니다.
    • 8코어(core)는 '명령어를 실행하는 부품'을 8개 포함하고 있다고 보면 됩니다

  • 코어를 여러 개 포함하고 있는 CPU를 멀티코어(multi-core) CPU 또는 멀티코어 프로세서라고 부릅니다.
    • 단일 코어일 때보다 속도가 더 빠름
    • 속도가 2.4GHz인 단일 코어 CPU와 클럭 속도가 1.9GHz인 멀티코어 CPU를 비교하면 일반적으로 후자의 성능이 더 좋습니다.
  • CPU 종류는 CPU안에 코어가 몇 개 포함되어 있는지에 따라 아래 표와 같이 싱글코어, 듀얼코어, 트리플코어 등으로 나뉩니다.

 

그러면 코어를 두 개, 세 개, 100개 ... 늘리면 연산 처리 속도도 두 배, 세 배, 100배로 빨라질까요?

정답은 ❌

 

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

학교에서 4인 1조로 조별 과제하는 것과 비슷합니다.

모두 똑같이 참여한다면 모르겠지만, 업무가 균등하게 분배되지 않거나 한 두 사람만 열심히 참여하면 결과적으로 한 두 사람만의 생산성만큼 결과물이 나오듯이 ㅎㅎ

 

코어마다 처리할 연산이 적절하게 분배되지 않는다면 코어 수에 비례하여 연산 속도가 증가하지 않습니다.

또한 처리하고자 하는 작업량보다 코어 수가 지나치게 많아도 성능에는 크게 영향이 없습니다.

 

100인분의 도시락은 한 명의 요리사가 만드는 것보다 열 명의 요리사가 만드는 것이 열 배가량 빠르겠지만,

4인분의 도시락은 열 명의 요리사가 만드는 게 다섯 명의 요리사가 만드는 것보다 특별히 더 빠르지 않은 것과 같습니다.

 

중요한 것은 코어마다 처리할 명령어들을 얼마나 적절하게 분배하느냐이고 그에 따라서 연산 속도는 크게 달라집니다.

 

📌 스레드와 멀티스레드

 

스레드는 프로그래밍 언어와 운영체제를 배울 때도 등장합니다.

 

스레드 thread의 사전적 의미는 '실행 흐름의 단위'입니다.

문자 그대로 받아들이지 말고 더욱 엄밀히 이해할 필요성이 있습니다.

CPU에서 사용되는 스레드와 프로그래밍에서 사용되는 스레드는 용례가 다르기 때문이지요.

 

 

📁 하드웨더적 스레드

스레드를 하드웨어적으로 정의하면 '하나의 코어가 동시에 처리하는 명령어 단위'를 의미합니다.

CPU에서 사용하는 스레드라는 용어는 보통 CPU 입장에서 정의된 하드웨어적 스레드를 의미합니다.

우리가 지금까지 배운 CPU는 1코어 1스레드 CPU였습니다.

즉, 명령어를 실행하는 부품이 하나 있고, 한 번에 하나씩 명령어를 실행하는 CPU를 가정했습니다.

 

 

 

반면, 여러 스레드를 지원하는 CPU는 하나의 코어로도 여러 개의 명령어를 동시에 실행할 수 있습니다.

예를 들어 2코어 4스레드 CPU는

아래 그림처럼 명령어를 실행하는 부품을 두 개 포함하고, 한 번에 네 개의 명령어를 처리할 수 있는 CPU를 의미합니다.

 

이 처럼 하나의 코어로 여러 명령어를 동시에 처리하는 CPU를 멀티스레드 프로세서 또는 멀티스레드 CPU라고 합니다.

 

더보기

8코어 16스레드는 

명령어 실행하는 부품을 8개 포함하고,

한 번에 16개의 명령어를 처리할 수 있는 CPU를 의미합니다.

 

이는 코어 하나당 두 개의 하드웨어 스레드를 처리한다는 뜻으로도 볼 수 있습니다.

멀티스레드와 함께 자주 접하게 될 용어로 하이퍼스레딩(hyper-threading)이라는 용어도 있습니다.

이는 인텔의 멀티스레드 기술을 의미합니다.

인텔이 자신들의 멀티스레드 기술에 하이퍼스레딩이라는 명칭을 부여한 것이지요.

📁 소프트웨어적 스레드

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

프로그래밍 언어나 운영체제를 학습할 때 접하는 스레드는 보통 이렇게 소프트웨어적으로 정의된 스레드를 의미합니다.

 

하나의 프로그램은 실행되는 과정에서 한 부문만 실행될 수도 있지만,

프로그램의 여러 부분이 동시에 실행될 수도 있습니다.

 

 

예를 들어, 워드 프로세서 프로그램을 개발한다고 가정해봅시다.

그리고 아래의 기능이 동시에 수행되길 우너한다고 해 봅시다.

  1. 사용자로부터 입력받은 내용을 화면에 보여 주는 기능
  2. 사용자가 입력한 내용이 맞춤법에 맞는지 검사하는 기능
  3. 사용자가 입력한 내용을 수시로 저장하는 기능

이 기능들을 작동시키는 코드를 각각의 스레드로 만들면 동시에 실행할수 있습니다.

(💡 Python, Java, C++ 등의 프로그래밍 언어를 이용해 소프트웨어적 스레드를 만들 수 있습니다.)

 

정리하면, 스레드의 하드웨어적 정의하는 '하나의 코어가 동시에 처리하는 명령어 단위'를 의미하고,

소프트웨어적 정의는 '하나의 프로그램에서 독립적으로 실행되는 단위'를 의미합니다.

 

한 번에 하나씩 명령어를 처리하는 1코어 1스레드 CPU도 소프트웨어적 스레드를 수십 개 실행 할 수 있습니다.

1코어 1스레드 CPU로도 프로그램의 여러 부분을 동시에 실행할 수 있죠.

 

만약 스레드의 사전적 정의(실행 흐름의 단위)만을 암기한다면 '1코어 1스레드 CPU가 여러 스레드로 만들어진 프로그램을 실행할 수 있다'라는 말이 어려울 것입니다.

이런 이유로 하드웨어적 스레드와 소프트웨어적 스레드는 구분하여 기억하는 것이 좋습니다.

 

📁 멀티스레드 프로세서

"하나의 코어로 여러 명령어를 동시에 처리하는 기술인 하드웨어적 스레드"에 대해서 더 알아봅시다.

+ 용어의 혼동을 방지하기 위해서 이제부터

소프트웨어적으로 정의된 스레드는 스레드

CPU에서 사용되는 스레드하드웨어 스레드라고 지칭하겠습니다.

 

멀티스레드 프로세서는 하나의 코어를 여러 명령어를 동시에 처리하는 CPU라고 했었죠?

어떻게 가능할 까요?

 

멀티스레드 프로세서는 실제로 설계하는 일은 매우 복잡하지만, 가장 큰 핵심은 레지스터입니다.

 

하나의 코어로 여러 명령어를 동시에 처리하도록 만들려면

프로그램 카운터, 스택 포인터, 메모리 버퍼 레지스터, 메모리 주소 레지스터와 같이 하나의 명령어를 처리하기 위해 꼭 필요한 레지스터를 여러 개 가지고 있으면 됩니다.

 

가령 프로그램 카운터가 두 개 있다면 '메모리에서 가져올 명령어 주소'를 두 개 지정할 수 있을 것이고,

스택 포인터가 두 개 있다면 두 개의 스택을 관리할 수 있겠죠?

 

레지스터 세트

 

하나의 명령어를 실행하기 위해 꼭 필요한 레지스터들을 편의상 '레지스터 세트'라고 표기하겠습니다.

레지스터 세트가 한 개인 CPU는 한 개의 명령어를 처리하기 위한 정보들을 기억할 뿐이지만,

레지스터 세트가 두 개인 CPU는 두 개의 명령어를 처리하기 위한 정보들을 기억할 수 있습니다.

 

📌 여기서 ALU제어장치두 개의 레지스터 세트에 저장된 명령어를 해석하고 실행하면

하나의 코어에서 두 개의 명령어가 동시에 실행됩니다.

 

하드웨어 스레드를 이용해 하나의 코어로도 여러 명령어를 동시에 처리할 수 있다고 했습니다.

그러나 메모리 속 프로그램 입장에서 봤을 때 하드웨어 스레드는 마치 '한 번에 하나의 명령어를 처리하는 CPU'나 다름없습니다.

가령 2코어 4스레드 CPU는 한 번에 네 개의 명령어를 처리할 수 있는데,

프로그램의 입장에서 봤을 땐 한 번에 하나의 명령어를 처리하는 CPU가 네 개 있는 것처럼 보입니다.

그래서 하드웨어 스레드논리 프로세서(Logical processor)라고 부르기도 합니다.

 

 

더보기

정리하자면

 

코어는 명령어를 실행할 수 있는 하드웨어 부품이고

스레드는 명령어를 실행하는 단위입니다.

멀티코어 프로세서는 명령어를 실행할 수 있는 하드웨어 부품이 CPU 안에 두 개 이상 있는 CPU를 의미하고

멀티스레드 프로세서는 하나의 코어로 여러 개의 명령어를 동시에 실행할 수 있는 CPU를 의미합니다.


2. 명령어 병렬 처리 기법

빠른 CPU를 만들려면 높은 클럭 속도에 멀티코어, 멀티스레드를 지원하는 CPU를 만드는 것도 중요하지만,

CPU가 놀지 않고 시간을 알뜰하게 쓰며 작동하게 만드는 것도 중요합니다.

 

명령어를 동시에 처리하여 CPU를 한시도 쉬지 않고 작동시키는 기법인 명령어 병렬 처리 기법 ILP; Instruction-Level Parallelism을 알아봅시다.

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

 

📌 명령어 파이프라인

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

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

(물론 이 단계가 정답은 아니지만, 전공서에 따라 명령어 인출 ➡️ 명령어 실행으로 나누기도 하고,

명령어 인출 ➡️ 명령어 해석 ➡️ 명령어 실행 ➡️ 메모리 접근 ➡️ 결과 저장으로 나누기도 합니다.)

 

여기서 중요한 점은 같은 단계가 겹치지만 않는다면 CPU는 '각 단계를 동시에 실행할 수 있다'는 것입니다.

예를 들어, CPU는 한 명령어를 '인출'하는 동안에 다른 명령어를 '실행'할 수 있고, 한 명령어가 '실행'되는 동안에 연산 결과를 '저장'할 수 있습니다.

 

 

이처럼 마치 공장 생산 라인과 같이 명령어들을 명령어 파이프라인 Instruction pipeline에 넣고

동시에 처리하는 기법명령어 파이프라이닝 instruction pipelining이라고 합니다.

 

 

명령어 파이프라인을 사용하지 않고 모든 명령어를 순차적으로 처리한다면 아래와 같이 처리했을 것입니다.

한눈에 봐도 명령어 파이프라이닝을 이용하는 것이 더 효율적임을 알 수 있죠?

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

이러한 상황을 파이프라인 위험 pipeline hazzard이라고 합니다.

 

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

파이프라인 위험

📁 데이터 위험 data hazzard

명령어 간 '데이터 의존성'에 의해 발생합니다.

모든 명령어를 동시에 처리할 수는 없습니다.

어떤 명령어는 이전 명령어를 끝가지 실행해야만 비로소 실행할 수 있는 경우가 있습니다.

 

위의 경우 명령어 1을 수행해야만 명령어 2를 수행할 수 있습니다.

명령어 2는 명령어 1의 데이터에 의존적입니다.

이처럼 데이터 의존적인 두 명령어를 무작정 동시에 실행하려고 하면 파이프라인이 제대로 작동하지 않는 것을 '데이터 위험'이라고 합니다.

 

 

📁 제어 위험 control hazzard

주로 분기 등으로 인한 '프로그램 카운터의 갑작스러운 변화'에 의해 발생합니다.

기본적으로 프로그램 카운터는 '현재 실행 중인 명령어의 다음 주소'로 갱신됩니다.

하지만 프로그램 실행 흐름이 바뀌어 명령어가 실행되면서 프로그램 카운터 값에 갑작스러운 변화가 생긴다면

명령어 파이프라인에 미리 가지고 와서 처리 중이었던 멸영어들은 아무 쓸모가 없어집니다.

이를 '제어 위험'이라고 합니다.

 

참고로 이를 위해 사용하는 기술 중 하나가 분기 예측 branch prediction입니다.

분기 예측은 프로그램이 어디로 분기할지 미리 예측한 후 그 주소를 인출하는 기술입니다.

 

📁 구조적 위험 structural hazzard

명령어들을 겹처 실행하는 과정에서 서로 다른 명령어가 동시에 ALU, 레지스터 등과 같은 CPU 부품을 사용하려고 할 때 발생합니다.

구조적 위험은 자원 위험 resource hazzard이라고도 부릅니다.

 

📌 슈퍼스칼라

파이프라이닝은 단일 파이프라인으로도 구현이 가능하지만,

오늘날 대부분의 CPU에서는 여러 개의 파이프라인을 이용합니다.

이처럼 CPU 내부에 여러 개의 명령어 파이프라인을 포함한 구조슈퍼스칼라 superscalar라고 합니다.

 

 

명령어 파이프라인을 하나만 두는 것이 마치 공장 생산 라인을 한 개 두는 것과 같다면,

슈퍼스칼라는 공장 생산 라인을 여러 개 두는 것과 같습니다.

 

 

슈퍼스칼라 구조로 명령어 처리가 가능한 CPU를 슈퍼스칼라 프로세서 또는 슈퍼스칼라 CPU라고 합니다.

슈퍼스칼라 프로세서는 매 클럭 주기마다 동시에 여러 명령어를 인출할 수도, 실행할 수도 있어야 합니다.

가령 멀티스레드 프로세서한 번에 여러 명령어를 인출하고, 해석하고, 실행할 수  있기 때문에 슈퍼스칼라 구조를 사용할 수 있습니다.

 

슈퍼스칼라 프로세서는 이론적으로 파이프라인 개수에 비례하여 프로그램 처리 속도가 빨라집니다.

하지만 파이프라인 위험 등의 예상치 못한 문제가 있어 실제로는 반드시 파이프라인 개수에 비례하여 빨라지지는 않습니다.

 

이 때문에 슈퍼스칼라 방식을 차용한 CPU는 파이프라인 위험을 방지하기 위해 고도로 설계되어야 합니다.

여러 개의 파이프라인을 이용하면 하나의 파이프라인을 사용할 때보다 데이터 위험, 제어 위험, 자원 위험을 피하기가 더욱 까다롭기 때문이지요.

 

📁 비순차적 명령어 처리 OoOE; Out-of-order execution

보통 OoOE로 줄여부르는 이 기법은 많은 전공서에서 다루지는 않지만, 오늘날 CPU 성능 향상에 크게 기여한 기법이자 대부분의 CPU가 차용하는 기법이기에 꼭 알아 두는 것이 좋습니다.

 

비순차적 명령어 처리 기법은 이름에서도 알 수 있듯 명령어들을 순차적으로 실행하지 않는 기법입니다.

명령어의 '합법적인 새치기'라고 볼 수 있지요.

 

 

앞의 코드를 이루는 명령어들 중에 서로 데이터 의존성이 전혀 없는, 순서를 바꿔 처리해도 수행 결과에 영향을 미치지 않는 명령어들이 있습니다.

가령 3번은 다음과 같이 뒤의 명령어 순서슬 바꾸어 실행해도 크게 문제될 것이 없습니다.

이렇게 순서를 바꿔 실행하면 아래와 같이 수행됩니다.

순차적으로 명령어를 처리할 때보다 더 효율적으로 처리되는 것을 알 수 있죠?

 

이렇게 명령어를 순차적으로만 실행하지 않고 순서를 바꿔 실행해도 무방한 명령어를 먼저 실행하여 명령어 파이프라인이 멈추는ㄱ ㅓㅅ을 방지하는 기법을 비순차적 명령어 처리 기법이라고 합니다.

 

하지만, 아무 명령어나 순서를 바꿔서 수행할 수는 없습니다.

 

 

위 코드에서 3번 명령어와 1번 명령어의 순서를 바꿀 수는 없겠죠?

3번 명령어를 수행하려면 반드시 M(100) 값이 결정되어야 하니까요.

마찬가지로 4번 명령어와 1번 명령어는 순서를 바꿀 수 없습니다.

1번 명령어를 토대로 3번 명령어가 수행되고,

3번 명령어를 토대로 4번 명령어가 수행되니까요.

 

하지만 위 코드에서 4번 명령어와 5번 명령어는 순서를 바꾸어 실행할 수 있습니다.

다시 말해 이 두 명령어는 어떤 의존성도 없기에 순서를 바꿔도 전체 프로그램의 실행 흐름에는 영향이 없습니다.

 

 

이처럼 비순차적 명령어 처리가 가능한 CPU는 명령어들이 어떤 명령어와 데이터 의존성을 가지고 있는지, 순서를 바꿔 실행할 수 있는 명령어에는 어떤 것들이 있는지를 판단할 수 있어야 합니다.

 

 


3. CISC와 RISC

명령어 파이프라이닝과 슈퍼스칼라 기법을 실제로 CPU에 적용하려면 명령어가 파이프라이닝에 최적화되어 있어야 합니다.

쉽게 말해 CPU가 파이프라이닝과 슈퍼스칼라 기법을 효과적으로 사용하려면

CPU가 인출하고 해석하고 실행하는 명령어가 파이프라이닝 하기 쉽게 생겨야 합니다.

 

'파이프라이닝 하기 쉬운 명령어'란 무엇일까요?

이와 관련해 CPU의 언어인 ISA와 각기 다른 성격의 ISA를 기반으로 설계된 CISC와 RISC에 대해서 확인해보겠습니다.

📌 명령어 집합 Instruction set

명령어의 생김새와 주소 지정 방식등을 이전 포스팅에서 다뤘습니다.

그리고 CPU는 명령어를 실행한다고 했었습니다.

 

그런데 한 가지 의문이 듭니다.

이 세상 모든 CPU들이 똑같이 생긴 명령어를 실행할까요?

 

세상에는 수 많은 CPU 제조사들이 있고, CPU마다 규격과 기능, 만듦새가 다 다른데, 모든 CPU가 이해하고 실행하는 명령어들이 다 똑같이 생겼을까요?

 

정답은 ❌

명령어의 기본적인 구조와 작동 원리는 크게 벗어나지 않지만, 명령어의 세세한 생김새, 명령어로 할 수 있는 연산, 주소 지정 방식 등은 CPU마다 조금씩 차이가 있습니다.

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

즉, CPU 마다 ISA가 다를 수 있다는 것입니다.

 

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

x86(x86-64)과 ARM은 다른 ISA이기 때문에 인텔 CPU를 사용하는 컴퓨터와 아이폰은 서로의 명령어를 이해할 수 없습니다.

실행 파일은 명령어로 이루어져 있고, 서로의 컴퓨터가 이해할 수 있는 명령어가 다르기 때문입니다.

💡 x86은 32비트, x86-64는 64비트용 x86 ISA입니다.

 

 

어셈블리어는 명령어를 읽기 편하게 표현한 언어라고 했습니다.

ISA가 다르다는 건 CPU가 이해할 수 있는 명령어가 다르다는 뜻이고,

명령어가 달라지면 어셈블리어도 달라집니다.

 

다시 말해 같은 소스 코드로 만들어진 같은 프로그램이라 할지라도

ISA가 다르다면

CPU가 이해할 수 있는 명령어도

어셈블리어도 달라진다는 것을 말합니다.

 

예를 들어, 동일한 소스 코드를 작성하고 ISA가 다른 컴퓨터에서 어셈블리어로 컴파일하면 위와 같은 결과를 얻을 수 있습니다.

왼쪽은 x86-64 ISA, 오른쪽은 ARM ISA입니다.

똑같은 코드로 만든 프로그램임에도 CPU가 이해하고 실행할 수 있는 명령어가 달라 어셈블리어도 다른 것을 알 수 있습니다.

 

참고로 사용한 컴파일러에 따라서도 어셈블리어가 달라질 수 있는데,

위 예시에서는 gcc 11.2라는 동일한 컴파일러를 이용했습니다.

 

ISA가 같은 CPU끼리는 서로의 명령어를 이해할 수 있지만,

ISA가 다르면 서로의 명령어를 이해하지 못합니다.

이런 점에서 볼 때 ISA는 일종의 CPU의 언어인 셈입니다.

 

CPU가 이해하는 명령어들이 달라지면 비단 명령어의 생김새만 달라지는 게 아닙니다.

ISA가 다르면 그에 따른 나비 효과로 많은 것이 달라집니다.

제어장치가 명령어를 해석하는 방식, 사용되는 레지스터의 종류와 개수, 메모리 관리 방법 등이 많은 것들이 달라지지요.

그리고 이는 곧 CPU 하드웨어 설계에도 큰 영향을 미칩니다.

 

우리가 실행하는 프로그램은 명령어로 이루어져 있지만,

ISA는 CPU의 언어임과 동시에 CPU를 비롯한 하드웨어가 소프트웨어를 어떻게 이해할지에 대한 약속이라고도 볼 수 있습니다.

 

 

앞서 설명한 명령어 병렬 처리 기법들을 적용하기에 용이한 ISA가 있고, 그렇지 못한 ISA가 있습니다.

다시 말하자면 명령어 파이프라인, 슈퍼스칼라, 비순차적 명령어 처리를 사용하기게 유리한 명령어 집합이 있고,

그렇지 못한 명령어 집합이 있습니다.

 

그렇다면 명령어 병렬 처리 기법을 도입하기 유리한 ISA은 무엇일까요?

 

📌 CISC; Complex Instruction Set Computer

'복잡한 명령어 집합을 활용하는 컴퓨터'를 의미합니다.

여기서 컴퓨터를 'CPU'라고 생각해도 좋습니다.

CISC란 이름 그대로 복잡하고 다양한 명령어들을 활용하는 CPU 설계 방식입니다.

앞서 ISA의 한 종류로 소개한 x86, x86-64는 대표적인 CISC기반의 ISA입니다.

 

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

메모리에 접근하는 주소 지정 방식도 다양해서 아주 특별한 상황에서만 사용되는 독특한 주소 지정 방식들도 있죠.

 

 

다양하고 강력한 명령어를 활용한다는 말

➡️ 상대적으로 적은 수의 명령어로도 프로그램을 실행할 수 있다는 것

 

x86-64 코드 길이가 ARM보다 짧은 것을 볼 수 있습니다.

이는 ARM 명령어 여러 개로 수행할 수 있는 일을 x86-64 명령어 몇 개만으로도 수행할 수 있음을 알 수 있습니다.

 

 

💡 프로그램을 실행하는 명령어 수가 적다는 말은

'컴파일된 프로그램의 크기가 작다'는 것을 의미합니다.

같은 소스 코드를 컴파일해도 CPU마다 생성되는 실행 파일의 크기가 다를 수 있다는 것이죠.

 

 

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

'적은 수의 명령어만으로도 프로그램을 동작시킬수 있다'는 점은 메모리 공간을 절약할 수 있다는 장점이기 때문입니다.

 

그러나 치명적인 단점이 있습니다.

활용하는 명령어가 워낙 복잡하고 다양한 기능을 제공하는 탓에 명령어 크기와 실행되기까지의 시간이 일정하지 않습니다.

그리고 복잡한 명령어 때문에 명령어 하나를 실행하는 데 여러 클럭 주기를 필요로 합니다.

 

이는 명령어 파이프라인을 구현하는 데 큰 걸림돌이 됩니다.

 

CISC가 활용하는 명령어는 명령어 수행 시간이 길고 가지각색이기 때문에 파이프라인이 효율적으로 명령어를 처리할 수 없습니다.

한마디로 규격화되지 않은 명령어가 파이프라이닝을 어렵게 만든셈이죠.

 

명령어 파이프라인이 제대로 동작하지 않는다는 것은 현대 CPU에서 아주 치명적인 약점입니다.

현대 CPU에서 명령어 파이프라인은 높은 성능을 내기 위해 절대 놓쳐서는 안 되는 핵심 기술이기 때문입니다.

 

게다가 CISC가 복잡하고 다양한 명령어를 활용할 수 있다고는 하지만,

사실 대다수의 복잡한 명령어는 그 사용 빈도가 낮습니다.

CISC 명령어 집합이 다양하고 복잡한 기능을 지원하지만 실제로는 자주 사용되는 명령어만 쓰였다는 것입니다.

 

더보기

1974년 IBM 연구소의 존 코크 John Cocke는 CISC 명령어 집합 중

불과 20% 정도의 명령어가 사용된 전체 명령어의 80%가량을 차지한다는 것을 증명하기도 했습니다.

 

더 풀어서 말하자면

 

📌 80/20 법칙의 발견

존 코크는 프로세서가 실제로 어떤 명령어들을 자주 사용하는지 분석했고, 매우 흥미로운 패턴을 발견했습니다:

- 프로세서가 지원하는 전체 200개 정도의 명령어 중
- 단 10개 정도의 기본적인 명령어들이
- 전체 처리 시간의 2/3를 차지했습니다

📌 실제 의미

이를 실용적 관점에서 보면:

 

CISC 프로세서의 경우
- 복잡하고 다양한 명령어들을 많이 지원하지만
- 실제로는 그 중 약 20%의 간단한 명령어들만 자주 사용됩니다
- 나머지 80%의 복잡한 명령어들은 거의 사용되지 않습니다

 



📌 영향

이 발견은 프로세서 설계에 혁명적인 변화를 가져왔습니다:

- 자주 사용되는 소수의 핵심 명령어들만 최적화하면 전체적인 성능 향상을 얻을 수 있다는 것을 보여줬습니다
- 이는 RISC(Reduced Instruction Set Computing) 아키텍처의 기초가 되었습니다
- 불필요하게 복잡한 명령어들을 제거하고 자주 사용되는 기본 명령어들의 실행 속도를 높이는 방향으로 프로세서 설계가 변화했습니다

 

 

정리하면, CISC 명령어 집합은 복잡하고 다양한 기능을 제공하기에 적은 수의 명령으로 프로그램을 동작시키고 메모리를 절약할 수 있지만,

명령어의 규격화가 어려워 파이프라이닝이 어렵습니다.

그리고 대다수의 복잡한 명령어는 그 사용 빈도가 낮습니다.

이러한 이유로 CISC 기반 CPU는 성장에 한계가 있습니다.

 

📌 RISC; Reduced Instruction Set Computer

CISC의 한계가 우리들에게 준 교훈은 아래와 같습니다.

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

이러한 원칙하에 등장한 것이 RISC입니다.

이름처럼 RISC는 CISC에 비해 명령어의 종류가 적습니다.

그리고 CISC와는 달리 짧고 규격화된 명령어, 되도록 1클럭 내외로 실행되는 명령어를 지향합니다.

즉, RISC는 고정 길이 명령어를 활용합니다.

 

 

 

RISC 명령어 집합은

명령어가 규격화되어 있고, 하나의 명령어가 1클럭 내외로 실행되기 때문에

명령어 파이프라이닝에 최적화되어 있습니다.

 

 

 

그리고 RISC는 메모리에 직접 접근하는 명령어를 load, store 두 개로 제한할 만큼 메모리 접근을 단수화하고 최소화를 추구합니다.

그렇기 때문에 CISC보다 주소 지정 방식의 종류가 적은 경우가 많습니다.

 

  • RISC
    • 메모리 접근을 단순화, 최소화
      • 대신에, 레지스터를 적극적으로 활용합니다.
        • 그렇기에 CISC보다 레지스터를 이용하는 연산이 많고
        • 일반적인 경우보다 범용 레지스터 개수도 더 많습니다.
    • 다만, 사용 가능한 명령어 개수가 CISC보다 적기 때문에
      • RISC는 CISC보다 많은 명령으로 포르그램을 작동시킵니다.
      • (명령어 개수는 많고, 명령어 종류는 적음)
    • ARM은 RISC 기반의 대표적인 ISA입니다.

 


 

📌 키워드로 정리하는 핵심 포인트

  • 빠른 CPU를 위한 설계 기법
    • 클럭 속도가 높은 CPU는 빠르게 작동합니다.
    • 코어란 CPU 내에서 명령어를 실행하는 부품입니다.
    • 멀티코어 프로세서란 여러 개의 코어를 포함하는 CPU를 말합니다.
    • 스레드에는 하드웨어적 스레드와 소프트웨어적 스레드가 있습니다.
    • 멀티스레드 프로세서란 하나의 코어로 여러 개의 명령어를 동시에 실행할 수 있는 CPU를 말합니다.
  • 명령어 병렬 처리 기법 
    • 명령어 파이프라이닝은 동시에 여러 개의 명령어를 겹처 실행하는 기법입니다.
    • 슈퍼스칼라는 여러 개의 명령어 파이프라인을 두는 기법입니다.
    • 비순차적 명령어 처리 기법은 파이프라인의 중단을 방지하기 위해 명령어를 순차적으로 처리하지 않는 기법입니다.
  • CISC와 RISC
    • ISA는 CPU의 언어이자 하드웨어가 소프트웨어를 어떻게 이해할지에 대한 약속입니다.
    • CISC는 복잡하고 다양한 종류의 가변 길이 명령어 집합을 활용합니다.
    • RISC는 단순하고 적은 종류의 고정 길이 명령어 집합을 활용합니다.