본문 바로가기

Lecture & Column/cpu_lec_col

파이프라이닝의 이해

Author : Daeguen Lee

(Any action violating either copyright laws or CCL policy of the original source is strictly prohibited)

 


 


(그림 출처: 위키피디아)

명령어가 수행되는 과정을 아래와 같다고 칩시다.

인출 - 디코드 - 실행 - 쓰기(완료)

이 네가지 과정은 각각 해당 과정의 기능에 맞는 하드웨어에 의해 수행되고
이러한 하드웨어의 묶음으로서 명령어는 온전히 처리되게 됩니다.
이렇게 명령어를 처리할 수 있는 하드웨어의 묶음을 '파이프라인'이라고 합시다.

※ 사실 파이프라인이라는 개념은 '파이프라인 스테이지'가 전제된 '파이프라이닝'이란 기법에서 파생되었지만
현재는 '파이프라인' 자체는 '명령어가 들르는 경로'와 동의어로 쓰이기 때문에 선/후를 따질 필요가 없습니다.


파이프라이닝되지 않은 프로세서는, 다시 말해 파이프라인 스테이지가 분절되지 않은 프로세서는
한 명령어가 인출 - 디코드 - 실행 - 쓰기가 끝나기 전에는 다른 명령어가 파이프라인에 투입될 수 없습니다.
다시 말해 위의 네 단계에 해당하는 하드웨어가 모두 명령어 하나에 매달려, 그 명령어가 끝나기만을 기다린다는 것이죠.

하지만 어떤 명령어가 실행 단계로 넘어왔다면 사실 인출/디코드 하드웨어가 그 명령어와 씨름할 일은 전혀 없습니다.
이렇게 '더 이상 그 명령어와 볼 일이 없음에도 불구하고' 그 명령어가 완료되기만을 기다리는 비효율을 극복하기 위해
파이프라인을 각 단계별로 나눠, 각 단계별로 명령어를 투입하는 '파이프라이닝'이라는 기법이 도입되었습니다.


명령어의 수행 과정을 직관적으로 따라가기 위해,
인출 / 디코드 / 실행 / 쓰기 단계가 각각 파이프라인 스테이지로 구분되었다고 가정합시다.


파이프라이닝되지 않은 (non-pipelined) 프로세서가 명령어를 처리할 때에는
각 명령어가 인출-디코드-실행-쓰기 과정을 완료할 때까지 다음 명령어가 기다려야 하므로
매 4사이클마다 한개의 명령어를 처리할 수 있습니다. 즉 이 경우의 스루풋은 0.25명령어/클럭이 됩니다.
(※ 파이프라이닝되지 않은 프로세서의 각 작업단계를 뭉뚱그려 "거대한 하나의 파이프라인 스테이지"로 볼 수도 있고
이렇게 보면 매 클럭당 한개의 명령어가 처리되는 셈인데, 이 관점에서는 클럭 주기가 4배 길어지는 셈이 됩니다)


반면 위의 네 단계가 각각의 파이프라인 스테이지로 분절된 '파이프라이닝된' 프로세서의 경우,
어떤 명령어가 '인출' 스테이지를 마치고 '디코드' 스테이지로 넘어가면
인출 스테이지는 놀고 있는 것이 아니라 바로 그 다음 명령어를 '인출'하는 작업에 착수할 수 있습니다.
마찬가지로 최초의 명령어가 디코드 스테이지에서 벗어나 실행 스테이지로 넘어가면
디코드 스테이지는 그 다음 명령어를 인출 스테이지로부터 받아서 디코드 작업을 쉬는 시간 없이 할 수 있게 되고
인출 스테이지는 역시 낭비되는 시간 없이 세 번째 명령어를 인출하는 작업에 착수할 수 있게 되는 것이죠.

결국 아래와 같은 산출량을 보이게 됩니다.

<클럭 -> 명령어 :: 프로세서 내부 :: 완료>

1클럭 -> ① :: ○○○○ ::
2클럭 -> ② :: ①○○○ ::
3클럭 -> ③ :: ②①○○ ::
4클럭 -> ④ :: ③②①○ ::
5클럭 -> ⑤ :: ④③②① ::
6클럭 -> ⑥ :: ⑤④③② :: ①
7클럭 -> ⑦ :: ⑥⑤④③ :: ②①
8클럭 -> ⑧ :: ⑦⑥⑤④ :: ③②①
9클럭 -> ⑨ :: ⑧⑦⑥⑤ :: ④③②①
10클럭 -> ⑩ :: ⑨⑧⑦⑥ :: ⑤④③②①

이런 식으로, 최초의 명령어가 모든 파이프라인 스테이지를 완주하기까지 걸리는 딜레이를 제외하면
그 뒤론 매 사이클마다 명령어를 1개씩 산출해낼 수 있게 됩니다.
파이프라인이 구현되지 않은 경우(아래)와 비하면 4배 가까이 생산성이 올라가는 셈입니다.

1클럭 -> ① :: ○○○○ ::
2클럭 -> ② :: ①○○○ ::
3클럭 -> ② :: ○①○○ ::
4클럭 -> ② :: ○○①○ ::
5클럭 -> ② :: ○○○① ::
6클럭 -> ③ :: ②○○○ :: ①
7클럭 -> ③ :: ○②○○ :: ①
8클럭 -> ③ :: ○○②○ :: ①
9클럭 -> ③ :: ○○○② :: ①
10클럭 -> ④ :: ③○○○ :: ②①


그렇다면 파이프라인을 무작정 '깊게'만드는 것이 좋은 일일까요?
앞에서 정의한 '파이프라인 스테이지'란 '명령어의 수행 과정을 단계별로 분절한 것' 입니다.
명령어가 모든 스테이지를 완주한 때에만 비로소 '완료'되는 것이며
바꾸어 말해, 여러 스테이지 중 하나라도 마치지 못할 결격사유가 있는 때에는
나머지 모든 스테이지에서의 시간 체류와 관계없이 그 명령어는 완료되지 못하게 됩니다.
사실 파이프라이닝의 함정은 여기에 있습니다.


명령어가 특정 파이프라인 스테이지를 마치지 못해 공백이 되는 경우를 파이프라인 버블이라고 합니다.
버블이 있는 경우 그 버블이 채워질 때까지 파이프라인으로의 투입은 순연됩니다.

1클럭 -> ① :: ○○○○ ::
2클럭 -> ② :: ①○○○ ::
3클럭 -> ② :: ○①○○ :: <- 여기서 명령어 2 버블 발생 (인출 단계)
4클럭 -> ③ :: ②○①○ ::
5클럭 -> ④ :: ③②○① ::
6클럭 -> ⑤ :: ④③②○ :: ①
7클럭 -> ⑥ :: ⑤④③② :: ①
8클럭 -> ⑦ :: ⑥⑤④③ :: ②①
9클럭 -> ⑧ :: ⑦⑥⑤④ :: ③②①
10클럭 -> ⑨ :: ⑧⑦⑥⑤ :: ④③②①

인출 단계에서 버블이 생긴 예를 들어 보았는데, 사실 이런 경우는 매우 흔한 편입니다.
(L1 캐시 미스로 L2 캐시, L3 캐시, 심지어 메모리까지 접근해야 할 경우 수십ns의 공백이 발생하는데
이 시간을 프로세서의 클럭으로 환산하면 수십~수백 클럭에 해당하는 버블이 들어가는 셈입니다)


즉 각 파이프라인 스테이지는 명령어의 '완료'에 상호 의존적이기 때문에
파이프라인 스테이지가 늘어날수록 (= 파이프라인이 깊어질수록) 완료율이 떨어지는 비율이 커지게 됩니다.

한편 파이프라인 스테이지가 늘어난다 (= 파이프라인이 깊어진다) 는 것은 어떤 의미일까요?

맨 처음 이야기했던 명령어의 수행 단계를 되새겨 봅시다.

인출 -> 디코드 -> 실행 -> 쓰기

사실 이 네 단계는 본질적으로 어떤 프로세서에서든지, 어떤 연산에서든지 마찬가지로 준수되는 과정입니다.
파이프라인을 깊게 한다는 것은 이들 단계를 세분화해 명령어가 거쳐 가는 단계를 늘리는 것이 그 본질인데
얼핏 불필요해 보일 정도로 명령어의 여정을 길게 늘리는 이유가 무엇인지 궁금한 분이 계실 겁니다.
그 이유는, 파이프라인 스테이지가 잘게 쪼개질수록 클럭을 높이기 쉽기 때문입니다.

인출 -> 디코드 -> 실행 -> 쓰기의 각 단계가 10ns씩을 점유한다고 생각해 봅시다.

파이프라이닝되지 않은 프로세서는 명령어 하나를 처리하기 위해 무려 40ns를 허비해야 하지만
가장 직관적으로 인출 / 디코드 / 실행 / 쓰기의 4 스테이지 파이프라인을 구현한 프로세서는
명령어 하나를 매 10ns마다 처리해 산출할 수 있습니다.

그렇다면 각 단계를 다시 상/하 공정으로 쪼개면 어떨까요?

인출(상) -> 인출(하) -> 디코드(상) -> 디코드(하) -> ... 이런 식으로 말이죠.

그렇다면 각 파이프라인 스테이지에 소요되는 시간은 5ns로 줄어들고, 이 말은 곧
매 5ns마다 명령어를 하나씩 산출해 낼 수 있다는 뜻이 됩니다.

파이프라인 스테이지가 잘게 쪼개질수록 하나의 스테이지로 묶이는 작업은 작고 단순해지고
바로 이 때문에 깊은 파이프라인 프로세서의 클럭을 올리기 쉬운 것입니다.

.
.
.
.
.

여기까지 파이프라이닝의 개념에 대해 간단히 적어 보았습니다^^;;


요약하자면,

1. 파이프라인 갯수와 파이프라인 스테이지 갯수는 다른 개념이다.
그 전에, 보다 본질적으로, 파이프라인과 파이프라인 스테이지는 다른 개념이다.

2. 파이프라이닝된 프로세서의 산출량은 그렇지 않은 프로세서보다 대체로 높아진다.
다만 버블에 의해 명령어의 완료율 (completion rate) 은 나빠질 수 있는데... 이건 3번에서.

3. 파이프라이닝의 약점은 파이프라인 버블에 있다.
본질적으로 접근하자면, 모든 스테이지를 마칠 때까지는 명령어의 완료 여부가 확실하지 않기 때문이다.
(이전까지 아무리 많은 스테이지를 성공적으로 수행했더라도 단 한 스테이지에서 미스가 나면 버블 발생)

4. 파이프라이닝의 또다른 (혹은, 주된) 장점은 클럭을 높이기 쉽다는 것이다.

 

//

 

아래 위젯은 일종의 크라우드펀딩 플랫폼인 티스토리 '밀어주기' 서비스 위젯입니다. 100원부터 3000원까지의 범위 내에서 소액기부가 가능하며, 이런 형태의 펀딩이 성공적일 경우 '이해관계자로부터 독립된 벤치마크' 의 지속 가능한 원동력이 되리라 생각합니다. 물론 후원 없이 제 글을 읽어 주시는 것만으로도 저는 독자 여러분께 감사합니다 :)