명령어 병렬 처리 기법

명령어 병렬 처리 기법ILP:Instruction-Level Parallelism: 명령어를 빠르고 효율적으로 처리하기 위해 CPU를 한시도 쉬지 않고 작동시키는 기법

  • 명령어 파이프 라이닝
  • 슈퍼스칼라
  • 비순차적 명령어 처리

명령어 파이프라인

명령어 파이프라인을 이해하려면 하나의 명령어가 처리되는 전체 과정을 비슷한 시간 간격으로 나누어 보아야 한다. 명령어 처리 과정을 클럭 단위로 나누어 보면 일반적으로 다음과 같이 나눌 수 있다.

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

💡 전공서에 따라 명령어 인출 -> 명령어 실행으로 나누거나,
명령어 인출 -> 명령어 해석 -> 명령어 실행 -> 메모리 접근 -> 결과 저장으로 나누기도 한다.

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

이를 그림으로 표현하면 다음과 같다.

t1에는 명령어1, 2를 동시에 처리할 수 있고 t2에는 명령어1, 2, 3을 동시에 처리할 수 있다. 이처럼 명령어를 겹쳐서 수행하면 하나씩 실행하는 것보다 더 효율적으로 처리할 수 있다.

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

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

파이프라인 하자드

데이터 위험data hazard

데이터 위험은 명령어 간 '데이터 의존성'에 의해 발생한다.

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

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

제어 위험control hazard

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

구조적 위험structural hazard

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

슈퍼스칼라

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

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

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

비순차적 명령어 처리

비순차적 명령어 처리OoOE: Out-of-order execution 는 오늘날 CPU 성능 향상에 크게 기여한 기법이자 대부분의 CPU가 차용하는 기법으로 파이프라인 중단을 방지하기 위해 명령어들을 병렬 처리하는 기법이다.

만약 명령어가 아래 그림처럼 순차적으로만 실행 된다면 3번 명령어는 1번과 2번 명령어에 의존적이기 때문에 1, 2번 명령어 실행이 끝날 때까지 기다려야 한다.

하지만 서로 데이터 의존성이 없는, 순서를 바꿔 처리해도 수행 결과에 영향을 끼치지 않는 명령어의 순서를 바꿔 수행하면 순차적으로 명령어를 처리할 때보다 더 효율적으로 처리될 것이다.

0개의 댓글