2021년 한국 영화에 대한 내용은 파이프라인(영화) 문서 참고하십시오.
1. 개요
Pipeline한 데이터 처리 단계의 출력이 다음 단계의 입력으로 이어지는 형태로 연결된 구조를 가리킨다. 이렇게 연결된 데이터 처리 단계는 한 여러 단계가 서로 동시에, 또는 병렬적으로 수행될 수 있어 효율성의 향상을 꾀할 수 있다.
대표적인 파이프라인 구조는 다음과 같은 것들이 있다.
- 명령어 파이프라인: 같은 CPU 회로 안에서 여러 명령어들이 단계적으로 수행되는 것을 가리킨다. 각 명령어는 다시 페치, 디코딩, 연산 등의 세부 주기로 나뉘어 각 파이프라인 단계에 의해 수행된다.
- 그래픽스 파이프라인: 대부분의 그래픽 카드는 그래픽 처리 과정을 3차원 사영, 윈도 클리핑, 셰이딩, 렌더링 등으로 나누어 각각의 하부 모듈에서 병렬적으로 수행한다.
- 소프트웨어 파이프라인: 한 소프트웨어의 출력이 자동으로 다른 소프트웨어의 입력으로 연결될 경우 이를 소프트웨어 파이프라인이라고 한다. 유닉스 계열 운영체제에서 사용되는 파이프가 대표적이다.
2. 상세
디지털 집적회로는 스위치 역할을 하는 많은 수의 트랜지스터[1]가 연결된 회로로 구성되며, 이 회로들은 시스템 클럭에 의해 동기화되어 동작된다. 즉 시스템 클럭이 일정 횟수가 입력될 동안 트랜지스터 회로들은 소정의 처리작업을 완료하고 다음 처리동작이 가능한 상태가 되어야 한다. 이 과정에서 주의해야 할 요소는 다음과 같다.- 트랜지스터 자체의 스위칭 속도는 무한하지 않으며 물리적 한계가 존재한다. 이 한계를 예를 들어 100MHz라고 가정하자.
- 회로에서 트랜지스터들의 동작이 시작되는 위치에서부터 동작이 종료되는 위치까지 직렬 연결된 최대 단계가 100단계라고 가정한다.
- 그럴 경우 예로 든 회로는 각 트랜지스터의 동작속도가 100MHz라고 해도 전체 회로의 동기화는 직렬 연결된 최대 길이 100단계의 제약을 받기 때문에 회로에 입력할 수 있는 최대 클럭은 100MHz/100 = 1MHz가 된다.
그리고 어느 날 어떤 공돌이가 이런 멋진 생각을 하게 되었다.
"중간 중간에 동작 상태를 저장해 놓고 다음 클럭이 들어왔을 때 그 상태를 다음 단계로 넘겨주면 직렬연결 단계를 줄이게 되면서 동작클럭을 높일 수 있지 않을까?"
디지털 집적회로에서의 파이프라인이라는 개념이 탄생하는 순간이었다.
즉 중간에 동작 상태 저장 단계를 5개 추가했다고 가정하면, 같은 100MHz짜리 트랜지스터라고 해도 직렬연결의 단계는 100단계에서 최대 20단계로 줄어들게 되고, 해당 회로가 수용할 수 있는 클럭은 100MHz/20stage=5MHz까지 증가하게 된다.
CPU의 명령어 파이프라인에 대한 전통적인 설명은 4단 파이프라인 구조를 통한 것이며 대체로 다음과 같은 명칭을 가지고 있다.
- 1단계: fetch(명령어를 메모리에서 인출)
- 2단계: decode(명령어 해석)
- 3단계: execute(명령어 실행)
- 4단계: writeback(라이트백)
4단 파이프라인에서 작업 A와 B를 처리할 때(트랜지스터 40개, 동작속도 40Hz):
1사이클: fetch(A) → 2사이클 fetch(B),decode(A) → 3사이클 decode(B),execute(A) → 4사이클 execute(B),writeback(A,완료) → 5사이클 writeback(B,완료) |
쉽게 설명하자면 파이프라인이 도입되기 전에는 1명이 하나의 일을 처음부터 끝까지 처리했다면 파이프라인이 도입 이후에는 한가지 작업만 할수있는 사람 여러명을 차례대로 놔두고 하나의 큰 작업을 조그만 작업으로 쪼개서 다음사람에게 순서대로 넘기는 방식으로 실행한다. 이렇게되면 하나의 작업을 1명이 할때 걸리는 시간보다 다음 사람한테 넘기는 시간이 훨씬 짧기 때문에 클럭을 올리기 쉬워지게 되어 특정 조건에서 동작 속도를 크게 올릴 수 있다.
비유를 통해 좀 더 쉽게 생각해보자. 어떤 자동차 공장에 자동차를 대량으로 만들어달라는 주문이 들어왔다. 이 공장에는 자동차 생산 공정에 필요한 모든 장비가 일렬로 늘어서 있어서, 처음부터 끝까지 모두 선만 따라가면 되도록 설계되어 있다. 우선 이 공장은 금속 가공, 가공이 끝난 후 기계장치 제작, 그 후로 차체 제작, 차체 조립, 성능 테스트를 순서대로 모두 마쳐야 자동차를 출고한다고 가정하자. 첫 번째 차량을 만들기 위해 금속 가공을 시작하고, 가공이 끝난 후 기계 장치를 만들었으며, 차체를 만들고 이를 조립하여 성능을 테스트하고 출고했다. 그런데 문제는, 한 대를 만드는데에 온 정신이 팔려서 작업을 마친 공정은 다음 공정에 작업물을 넘겨주고는 그냥 놀고 있었다는것이다. 그리고는 차가 완성되어 출고되고 생산라인이 완전히 텅텅 비워질 때까지 새로운 차량의 제작 역시 시작하지 않는다. 이는 비효율적이다.
따라서, 공장장은 이러한 비효율적 운영을 피하기 위해 각각의 공정에서 작업이 끝나는대로 다음 공정으로 넘기고, 곧바로 이전 공정에서 작업을 받아 일을 하도록 시켰다. 이렇게 되면 모든 공정이 전부 착착 돌아가게 되어 더 효율적으로 자동차가 찍혀나오게 된다.
CPU도 마찬가지로, 1초에 수백만번의 명령어를 처리하는 요즘 컴퓨터들 역시 명령어를 "읽고", 이 명령어가 무슨 뜻인지 "이해"하고, 이해한 명령어를 "실행"하는 등 일단 읽고 넘기기만 하는 공정, 앞에서 받은 명령어를 이해만 하고 넘기는 공정, 또 앞에서 받은 명령어를 실행만 하는 공정 등등으로 비유해볼 수 있다. 만약 이렇게 착착 진행되지 않는다면 명령어 하나를 다 읽고 이해하고 실행해서 마무리가 될 때까지 다 놀고 있는데 아직 앞 명령어 처리가 안 끝났단 이유로 뒤에 줄 서있는 명령어들은 애꿎은 시간낭비만 하게 된다. 이것을 해결할 기술이 바로 파이프라이닝이다.
어느 날, 자동차를 열심히 조립하고 있던 한 일꾼이 크게 당황했다. 웬 오토바이 부품이 들어와서 지나가고 있었던 것이다. 뭔가 잘못됐음을 깨달은 일꾼은 이 차체를 그냥 지나가게 냅뒀다간 뒤쪽 공정에서도 시간낭비만 초래할 것임을 알아냈고, 차체 외부에 파란색 깃발을 꽂아 표시했다. 이는 "쓸모가 없거나 잘못 들어와 쓸 수 없는 부품"을 의미한다. 따라서 뒤쪽 공정에서 일하던 일꾼들도 파란 깃발이 꽂힌 차체를 보면 "이 차체는 뭔가 잘못됐구나" 하면서 이걸 들어내서 빼버리던가, 아니면 그냥 완성 지점까지 냅둬서 저 끝에서 걸러내도록 해서 이것 때문에 헛짓을 하지 않게 될 것이다. 이것을 CPU 파이프라이닝에서 "버블"이라고 부른다. 관련 용어 문단 참조.
3. 역사
파이프라인이라는 개념은 ENIAC이 처음 나오기 이전 시절인 1939년 Z1부터 ILLIAC II와 IBM Stretch 프로젝트를 한정으로 처음 사용되었고, 1970년대부터 슈퍼컴퓨터나 메인프레임에 본격적으로 도입되었으며, 개인용 컴퓨터는 1980년대 초 RISC의 태동과 궤적을 같이 하여 본격적으로 도입했을 것으로 추정된다. 이후 파이프라인 단계의 확장 역사는 다음과 같다.- 인텔 아키텍처의 파이프라인 스테이지
- 1971년 4004: 1단계
- 1972년 8008: 1단계
- 1974년 8080, 4040: 1단계
- 1976년 8085: 1단계
- 1978년 8086: 2단계 [2]
- 1982년 80286: 2단계
- 1985년 80386: 3단계 [3]
- 1989년 80486: 5단계 [4]
- P5
- 1993년 P5 (펜티엄1): 5단계 [5]
- 1994년 P54C (펜티엄1): 5단계
- 1995년 P54CQS, P54CS (펜티엄1): 5단계
- 1997년 P55C (펜티엄 MMX): 6단계
- P6
- 1995년 P6 (펜티엄 프로): 12~14단계 [6]
- 1997년 클라매스 (펜티엄 II): 12~14단계
- 1998년 데슈츠 (펜티엄 II), 드레이크 (펜티엄 II 제온): 12~14단계
- 1999년 카트마이, 코퍼마인 (펜티엄 III), 태너, 캐스케이드 (펜티엄 III 제온): 10~12단계
- 2001년 투알라틴 (펜티엄 III): 10~12단계
- 넷버스트
- 2000년 윌라멧 (펜티엄 4): 20단계
- 2001년 포스터 (제온): 20단계
- 2002년 노스우드 (펜티엄 4), 프레스토니아, 갤러틴 (제온): 20단계
- 2004년 프레스캇 (펜티엄 4), 노코나 (제온): 31단계
- 2005년 스미스필드 (펜티엄 D), 어윈데일, 크랜포드, 퍼토먹, 팩스빌 (제온): 31단계
2005년 테자스 (펜티엄 5), 제이호크 (제온): 40~50단계- 2006년 시더밀 (펜티엄 4), 프레슬러 (펜티엄 D), 뎀시, 털사 (제온): 31단계
- 2003년 P6 개량판: 10~12단계
- 2006년 코어, 펜린: 12~14단계
- 2008년 본넬, 설트웰: 13~16단계
- 2008년 네할렘, 웨스트미어: 16~20단계
- 2011년 샌디브릿지, 아이비브릿지: 14~19단계
- 2013년 하스웰, 브로드웰: 14~19단계
- 2013년 실버몬트, 에어몬트: ?
- 2015년 스카이레이크, 캐논 레이크(팜 코브): 14~19단계
- 2016년 골드몬트, 골드몬트 플러스: ?
- 2019년 서니 코브, 윌로 코브, 사이프러스 코브: 14~20단계
- 2020년 트레몬트: ?
- 2021년 골든 코브: ?
- 2021년 그레이스몬트: ?
2004년 펜티엄 4 프레스캇의 31단계를 기점으로 파이프라인 단계가 다시 축소되었다.
4. 한계
파이프라인 기법은 '모든 명령이 원하는대로 수행된다'는 것을 가정하기 때문에, 이 가정이 깨지는 순간 혼돈의 카오스가 터진다.우선 기존에 사용하던 메모리를 다시 사용할 때 부터 문제가 된다. 동일한 위치의 메모리를 사용하는데 '쓰기' 명령이 파이프라인에 어느 곳에 있고, '읽기' 명령은 다른 곳에 있는 경우 발생하기 쉬운데, 아직 파이프라인에서 처리되지 못한 데이터를 사용해야 하는 상황이 오기 때문이다. 이를 '데이터 위험'(Data Hazard), 또는 '데이터 의존성(Data Dependency)'이라 하는데, '읽고 나서 다시 읽는'(Read after Read) 것만 빼고는 어느 단계에서 '쓰든' 무조건 데이터 위험에 들어간다.[7][8] (약자를 따서 각 RAW, WAR, WAW 위험이라 한다. A는 After의 약자이고, W는 당연히 Write의 W이다.) 때문에 이러한 위험을 만난 경우 CPU 차원에서 No Operation(명령 없음)을 넣어 후속 명령을 사실상 파이프라인 밖으로 쫓아내고 있다. 또한 WAR, WAW Hazard는 레지스터 이름 변경(register renaming)을 통해 부분적으로나마 없애는 것이 가능하다. 따라서 실제로 데이터 위험이 존재하는 의존성은 RAW 하나로 볼 수 있다. 이는 프로그램의 흐름에 따라 결정되기 때문에 Flow Dependency라고도 하며 '진짜 위험'이라는 뜻으로 True Dependency라고도 한다.
분기 명령 예측에 실패하면 일은 걷잡을 수 없이 커진다. A를 가정하고 처리하고 있었는데 알고 보니 A가 아니라 B를 처리해야 했던 상황이라면, A로 가정하고 처리하던 것은 전부 버려야 한다.[9] 이를 Control Hazard라 하는데, 이 위험은 분기가 덕지덕지 붙은 프로그램에서 더욱 심하다. 심지어는 실제로 낭비되는 처리 단계가 아키텍처의 파이프라인 깊이보다도 훨씬 커질 수 있다. 실제로 31단계의 파이프라인이 있는 프레스캇의 경우 연속 예측 실패 4방이면 100단계가 그냥 낭비된다.
5. 해결 방법, 그러나 다시 한계
그래도 일반적인 환경에서는 평균적으로 큰 성능 향상이 있기에 많은 개선이 시도되었다.우선 위험성을 예측하는 알고리즘이 사용되고는 있지만, 그냥 지연시키는 방법의 복잡도가 인 마당에 이라서 최악의 경우에는 안하는 것만도 못하는 일도 발생한다.
또 다른 방법은 uOP 캐시를 이용하는 것인데, CPU의 프론트 엔드에서 프로그램의 명령어를 마이크로 명령어(uOP)로 해독하는 과정에서 명령어들을 적재하고 인덱싱하는 기법이다. 이 과정에서 uOP는 RISC의 특징을 따라가게 되는데, 이를 통해 전력 소모를 낮추면서도 단위 시간 당 처리 능력이 향상되었다. 실제로 이 기법이 적용된 스카이레이크의 경우, 같은 클럭의 프레스캇에 비해 효율이 높다. 하스웰에 대비해서는 약간 뒤쳐지긴 했지만, 이는 파이프라인과 uOP 증설을 동시에 처리하면서 발생한 것인 데다가, 공정미세화와 고도의 전력 컨트롤 기법을 통해 단위 클럭 당 전력 소모를 개선 시킨 것이라 모호하다.
그 외의 방법으로 명령어 및 데이터 선인출, 투기적실행, 분기 예측, 레지스터 재명명, 명령어 프리디코드 등의 여러 기법들이 개발되고 적용되어 파이프라인의 문제점들을 상당 부분 완화시키긴 했으나, 그러한 추가 기능을 하드웨어 차원에서 구현하다 보니 대량의 트랜지스터가 추가로 되면서 결국 CPU의 구조가 더욱 복잡해지는 결과를 낳았고, 2018년에 CPU게이트가 일어나는데 일조를 하게 되었다.
또한 파이프라인을 깊게 설계하면서 클럭을 끌어올리는 전략은 물리적인 법칙으로 인하여 발열 문제로 돌아왔고, 결국 2004년 경 4 GHz의 벽에 막히면서 중단되었다. 그 이후로 인텔은 31단계에 이르렀던 깊은 파이프라인을 다시는 시도하지 않게 된다. 게다가 무어의 법칙 마저 경제적, 물리학적 한계로 버린 이상, 현재의 깊이 마저 더욱 줄여야 할 형편이 되었다.
6. 의의
현대의 CPU 중에서 어느 정도 성능을 지향하도록 개발된 CPU 중 파이프라인 구조를 채택하지 않은 CPU는 사실상 없으며 특히 적극적인 파이프라인의 도입은 1980년대 말~2000년대 초에 경험했던 급격한 CPU 클럭의 향상과 그로 인한 CPU 성능 향상에 지대한 공헌을 하였다. 비록 2004년도 31단계라는 무지막지한 파이프라인 구조를 끝으로 파이프라인 단계 확대의 움직임은 제동이 걸린 상황이지만 그 이후의 CPU들 조차도 파이프라인 구조 없는 설계는 생각하기도 힘들다.하지만 수행 시간에 민감한 특수 아키텍처, 특히 군사 & 우주용 CPU 아키텍처라면 얘기가 달라진다. 파이프라인 자체가 안전성 면에서 취약한 구조이니 만큼 ms(밀리초), 심지어는 μs(마이크로초) 단위를 다투는 하드웨어에 쓰기에는 위험해도 너무 위험하다. 이 때문에 관련 업체들은 칩을 직접 생산하면서까지 파이프라인을 피하려 하고 있으며, 어느 정도의 지연을 허용한다 해도 3~4단계로 그치는 게 대부분이다. 구식 아키텍처에 기반한 호환 CPU가 여전히 생산되고 있는 것도 다 이러한 수요가 있어서 그렇다.
7. 관련 용어
- Deep Pipeline: 상술한 31단계 같은 단수가 높은 파이프라인을 칭하는 말. 그러나 후술할 버블을 과도하게 발생시키는 문제가 있다. CPU가 아닌 특수 목적용으로 설계되어 특정 연산을 수행하는데 최적화된 프로세서의 경우 처리량이 중요하고 해저드에 자유롭고 레이턴시에 덜 민감하다면 수천단계의 매우 깊은 파이프라인을 적용해 타이밍을 맞추면서 매우 복잡한 연산의 결과를 지속적으로 뽑아내기도 한다.
- Bubble: 딥 파이프라인 안에 No-Operation 혹은 딥 파이프라이닝으로 초래된 이미 변경되었으나 파이프 안에 들어온 데이터, 즉 쓸모 없이 CPU만 점유하고 있는 데이터를 칭한다. 관념적으로는 이런 버블을 빨리 파이프라인에서 빼 주어야 효율이 상승한다.
- No Operation: CPU파이프라인 전체가 모두 다 빌 때까지 인풋을 대기하는 어셈블리 명령어. 해저드를 회피할 수 있는 가장 강력한 수단이지만, 실질적으로 파이프라인의 의미를 퇴색시키고, 성능을 떨어트린다.
- Timing Closure: 디지털 회로 설계 과정에서 필수적으로 해야 하는 업무. 설계 중인 회로를 적절히 수정해서 Timing Failure를 해결하는 것을 Timing Closure라고 부른다. 클럭 관련 문제로 회로가 정상 동작하지 못하는 문제를 Timing Failure라고 하는데, 대표적으로 로직을 처리하는데 걸리는 딜레이가 클럭 사이클보다 충분히 짧지 못해서 발생하는 set up time violation이 있다. 이때 사용되는 테크닉 중 하나가 경로 사이 사이에 레지스터를 박아넣어 로직의 길이를 쪼개는 파이프라이닝이다.
8. 관련 항목
[1] 게이트라는 표현도 쓴다. 현용 대부분의 CPU의 집적회로가 CMOS 기술로 구현되며 CMOS의 트랜지스터의 가운데 극의 이름이 게이트이기 때문.[2] 이전 명령어의 실행이 끝난 다음 명령어를 인출하는 8085와 달리 명령어 실행 중 버스를 사용하지 않는 사이클동안 다음 명령어를 미리 인출(prefetch)하는 2단계 파이프라인 구조이다. 단, 이후 등장한 RISC 방식의 CPU와 달리 여러 개의 더 작은 명령어로 이뤄진 마이크로코드를 통해 x86 명령어 동작을 구현하였기 때문에 실행 단계에 1 사이클이 소요된다고 가정한 위의 예시와는 달리 8086의 경우 ADD와 같은 간단한 명령어의 경우에도 피연산자에 따라 3-31 사이클이 소요되고, 명령어에 따라 최소 2 사이클(CLC, HLT, ...)에서 많게는 204 사이클이 소요된다.(IDIV mem16, base+index+disp., segment override)[3] 명령어 인코딩이 복잡해짐에 따라 디코드와 실행을 별도의 stage로 분리하였다.[4] 실행 단계에서 최소 2사이클을 소모하였던 이전의 x86 CPU와는 달리 여러 명령어를 1 사이클만에 실행할 수 있게 되었다.[5] 슈퍼스칼라 구조를 도입하였다.[6] 비순차적 실행 및 내부적으로 RISC 구조를 도입하였다.[7] 간단하게 예를 들어보자. 여러분이 컴퓨터에게 "A는 3이고, B는 4이다. 여기서 B는 A에다가 8을 더한 값으로 수정하도록 하겠다. 그러면 B값은 얼마인가 계산해달라." 하는 프로그램을 짰다고 가정하자. 우리가 원하는 정답은 B = 11이다. 그러나 컴퓨터는 이것을 정답으로 내놓지 못할수도 있다. A는 3이고, B가 4이며, B값을 A에다가 8을 더한값으로 수정하는건 좋은데, B값을 최종적으로 계산해서 다시 저장하기 전에 B값이 얼마인지 묻는 명령이 들어오면 B값을 계산하긴 했으나 아직 저장하지 못했기 때문에 값이 최신화 되지 못하고 예전에 저장되어있던 값인 4를 답으로 내놓게 되고, 이는 오류가 되는 것이다.[8] 세 사람이 앉아있고 100미터쯤 떨어진 곳에 계산기가 놓여있는 책상이 있으며, 이 사이를 왔다갔다하는 일꾼이 있다고 가정하자. 첫 번째 사람은 명령을 외치기만 하고, 두 번째 사람은 숫자를, 세 번째 사람은 일꾼을 보내 계산을 시키거나 명령이 완료되면 "오케이!"라고 외친다. 첫 번째 명령이 들어오자, "숫자 저장", "A=3"이었고, 이를 알아들은 세 번째 사람이 오케이라고 외쳤다. 두 번째에는 "숫자 더하기", "8"이라고 명령이 들어오자, 세 번째 사람은 일꾼을 시켜 계산기로 뛰어가 3+8이 얼마인지 계산시킨다. 아직 일꾼이 계산기가 있는 책상으로 뛰어가고 있는 중인데, 세 번째 명령으로 "결과 계산", "A"라는 명령이 들어왔는데, 일꾼은 아직도 계산기를 두들기고 있고 정답을 아직 이쪽으로 갖고오지 못했다. 따라서 세 번째 사람의 메모지엔 아직 A=3이라고 적혀있었고, 결국 "오케이! 정답은 3!"이라고 말한다. 당연히 오류.[9] CPU 자체에는 저장소의 제약사항이 상당하므로 재활용도 할 수 없다. 때문에 처리가 틀렸다면 쓰던 자원을 가급적 빨리 버려야 하는데, 이것을 늦추다 보면 다음 명령을 처리하는 데에 시간 낭비가 심할 뿐만 아니라 해커의 공격에 취약해 질 수 있다.