명령어를 병렬로 처리는 방법의 종류는 무엇이 있을까?
명령어 병렬 처리 기법 (ILP, Instruction-Level Parallelism)
--
속도가 빠른 CPU를 만들기 위해
높은 클럭 속도, 멀티코어, 멀티스레드를 지원하는 CPU를 만드는 것도 중요하지만,
CPU가 놀지 않고 시간을 알뜰하게 사용하며 동작하도록 하는 것도 중요하다.
바로 명령어 병렬 처리 기법이
명령어를 병렬로 동시에 처리하는 방법으로
CPU가 쉬지 않고 끊임없이 동작하게 하는 기법이다.
대표적인 "명령어 병렬 처리 기법"
- 명령어 파이프라인(파이프라이닝)
- 슈퍼스칼라
- 비순차적 명령어 처리
--
명령어 파이프라인
--
명령어 파이프라인은
명령어 처리에 대한 과정을 각 단계로 분리하고
해당 단계를 동시에 실행할 수 있도록 하는 기술이다.
비유를 하자면
공장에서 컨베이어 벨트에 어떠한 물건을 두면
해당 컨베이어 벨트를 따라가며 "씻기" -> "말림" -> "도색" -> "포장"처럼 동작하는 방식과 비슷하다.
하나의 명령어가 처리되는 전체적인 과정을
일반적으로 클럭 단위로 나누어 보면 아래와 같은 단계로 나눌 수 있다.
- 명령어 인출 (Instruction Fetch)
- 명령어 해석 (Instruction Decode)
- 명령어 실행 (Execute Instruction)
- 결과 저장 (Write Back)
해당 단계는 일반적인 단계일 뿐
순서가 변경되어 명령어를 처리하는 경우도 존재한다.
명령어 파이프라인을 사용하지 않고
기존처럼 명령어를 순차적으로 처리한다면
아래의 그림처럼 하나의 명령어가 각 단계를 모두 수행한 다음에 다음 명령어에 대한 처리를 진행하게 된다.
이러한 경우 CPU에서 인출을 담당하는 부품은 다음 명령어의 인출을 동작하기 전까지 아무것도 안 하게 된다.
이렇게 CPU가 쉬지 않고 계속 일할 수 있도록 명령어 파이프라인 기법을 적용하면 아래와 같다.
위 그림처럼 "인출"을 하는 단계에서는 명령어 1의 인출을 수행하면 바로 다음 명령어 2의 인출을 진행하듯이
각 단계들은 현재 동작을 모두 수행하면 바로 다음 동작을 수행한다.
파이프라인이 동작하는 과정을 공장에 비유하면 아래와 같은 그림와 비슷하다.
다만 특정 상황에서는 "명령어 파이프라인"을 수행할 때 성능 향상에 실패하는 경우도 존재한다.
- 데이터 위험
- 제어 위험
- 구조적 위험
이러한 상황을 "파이프라인 위험(Pipeline Hazard)"라고 부른다.
데이터 위험 (Data Hazard)
명령어 파이프라인의 특정 단계에서 사용되는 데이터가 아직 준비되지 않은 상황에서 발생하는 위험이다.
이는
주로 명령어 간의 데이터 종속성 때문에 발생하게 된다.
예시로 아래와 같이 두 명령어가 존재한다고 가정하자.
- 명령어 1 : R1 = R2 + R3
- 명령어 2 : R4 = R1 + R5
여기서 명령어 2는 명령어 1의 결과인 R1이 필요하다.
즉, 명령어 2를 수행하기 위해서는 명령어 1이 모두 수행이 완료된 상태에서만 수행이 가능하다.
이렇게 서로 명령어의 데이터가 의존적이게 되면
명령어 파이프라인이 제대로 작동하지 않을 수 있으며 (명령어 파이프라인은 명령어들을 동시에 실행해야 함)
이를 "데이터 위험"이라고 부른다.
제어 위험 (Control Hazard)
프로그램의 흐름을 변경하는 명령어(분기, 점프 등)로 인해 발생하는 위험이다.
주로 "프로그램 카운터"의 갑작스러운 변화에 의해 발생하게 된다.
프로그램 카운터는
현재 실행 중인 명령어의 다음 주소를 가리키고 있으며
명령어 처리를 순차적으로 수행할 수 있도록 도와준다.
다만 해당 프로그램 카운터의 값이 갑작스럽게 변경하게 된다면
명령어 파이프라인에 미리 가지고 와서 처리 중이었던 명령어들은 아무 쓸모가 없어지게 된다.
- 현재 "프로그램 카운터"는 0x02에 위치한 명령어를 가리키고 있다.
- 원래 "프로그램 카운터"는 순차적으로 명령어를 가리키므로 알아서 다음 명령어들을 병렬로 실행 중이다.
- 갑자기 t2 단계에서 "프로그램 카운터"가 0x11로 변경
- 즉, 0x01번지의 명령어 다음으로 0x11번지의 명령어가 실행되어야 한다는 의미다.
- 그래서 병렬로 처리 중이었던 0x02, 0x03번지의 명령어 처리는 무용지물이 된다.
이를 방지하기 위한 기술 중에 하나로 "분기 예측 (Branch Prediction)"이 존재하다.
이는 프로그램이 어디로 분기할지 미리 예측한 후 해당 주소로 인출하는 기술이다.
구조적 위험 (Structural Hazard)
명령어 파이프라인 내에서 자원을 동시에 여러 명령어가 사용하려고 할 때 발생하는 위험이다.
즉, 동시에 실행하는 명령어들이 서로 동시에 CPU의 부품(ALU, 레지스터 등)을 사용할 때 발생한다.
그래서 "자원 위험 (Resource Hazard)"라고도 부른다.
--
슈퍼스칼라 (SuperScalar)
--
위에 설명한 명령어 파이프라인처럼
단일 파이프라인으로 구현하지 않고
여러 개의 파이프라인을 사용한 구조를 의미한다.
비유를 하자면
명령어 파이프라인은 공장에서 컨베이어 벨트를 한 개로 운영하는 것이고
슈퍼스칼라는 공자에서 컨베이어 벨트를 여러 개로 운영하는 것이다.
슈퍼스칼라 구조로 명령어 처리가 가능한 CPU를
"슈퍼스칼라 프로세서" or "슈퍼스칼라 CPU"라고 부른다.
이론적으로는
파이프라인 개수에 비례하여 프로그램 처리 속도가 빨라진다.
하지만 파이프라인 위험 등의 예상치 못한 문제들이 존재하기 때문에
반드시 파이프라인 개수에 비례하여 빨리지지는 않는다.
이러한 문제들 때문에
"슈퍼스칼라" 방식을 사용하는 CPU는
파이프라인 위험을 방지하기 위해 고도로 설계되어야 한다.
(하나의 파이프라인을 사용하는 방법보다 여러 개의 파이프라인을 사용하는 방법이 위험을 피하기 더 까다롭다.)
--
비순차적 명령어 처리 (OoOE, Out-of-Order Execution)
--
오늘날 CPU 성능 향상에 크게 이여한 기법으로
명령어들을 순차적으로 실행하지 않는 기법이다.
순차적으로 명령어들을 처리하다보면
파이프라인 위험과 같은 예상치 못한 문제들로 인해 현재 처리해야 하는 명령어를 바로 처리하지 못하는 경우가 존재한다.
그래서 모든 명령어를 순차적으로만 처리하고자 하면 해당 파이프라인은 멈추게 된다.
아래 명령어처럼 중간에 다른 명령어의 결과를 사용해야 하는 명령어가 존재한다고 가정하자.
- 0x01번지 명령어 : 1
- 0x02번지 명령어 : 2
- 0x03번지 명령어 : 0x01 + 0x02
- 0x04번지 명령어 : 1
- 0x05번지 명령어 : 2
순차적으로 명령어를 처리하는 과정
0x03번지의 명령어는 0x01, 0x02번지의 명령어에 대한 결과가 필요하므로
0x02번지의 명령어가 끝날 때까지 대기하고 있다가 동작하게 된다.
이로 인해 0x04, 0x05번지의 명령어도 같이 대기하게 된다.
비순차적으로 명령어를 처리하는 과정
0x01, 0x02번지의 명령어의 결과가 필요한 0x03번지 명령어는
0x01, 0x02번지의 명령어가 끝나는 시점에 실행될 수 있도록
순서를 변경하여 실행하도록 한다.
이렇게 명령어 파이프라인이 비효율적으로 멈추는 것을 방지하기 위해
순차적으로 명령어를 처리하지 않고 상황에 알맞게 순서를 바꿔 실행한다.
명령어 실행 순서를 아무거나 변경할 수는 없다.
0x01번지의 명령어와 0x03번지의 명령어의 순서를 바꿀 수 없다.
(0x03번지의 명령어를 수행하기 위해서는 0x01번지의 명령어가 반드시 완료되어야 하기 때문)
다만 의존성에 상관없는 명령어끼리는 순서를 변경할 수 있다.
--
'CS > 컴퓨터 구조' 카테고리의 다른 글
메모리 (RAM) (0) | 2024.08.29 |
---|---|
[CPU 성능 향상] ISA (+ CISC, RISC) (0) | 2024.08.28 |
[CPU 성능 향상] 클럭, 멀티코어, 멀티스레드 (0) | 2024.08.26 |
[CPU 작동 원리] 명령어 사이클과 인터럽트 (0) | 2024.08.25 |
[CPU 동작 원리] 레지스터 종류 (0) | 2024.08.24 |