벤치리뷰·뉴스·정보/아키텍처·정보분석

[분석정보] CPU 고속화의 기본 수단 파이프라인 처리의 기본 2/2

tware 2010. 9. 13. 21:00

 

CPU 성능 발휘를 저해하는 "파이프 라인 스톨"

 이전에 이어 이번에도 CPU의 파이프 라인에 대해 설명한다. 마지막의 마지막은 "파이프 라인 단수를 함부로 늘려도 문제" 라는 말을 했다. 이유 중 하나는 소비 전력이지만, 이것은 또 다른 이야기로 이번에는 또 하나의 이유인 "파이프 라인 스톨 '과'파이프 라인 해저드" 쪽을 올려보고 싶다.

 이 2개는 때때로 혼동 될 수도 있지만, 기본적으로는 다른 요인에서 발생하는 문제이며, 대응 방법도 조금 다르다. 먼저 전제로 [그림 1]과 같은 경우를 생각해 본다. 파이프 라인 단수는 10단으로 되어 있기 때문에 (요즈음의 x86 프로세서에서 말하면 짧은),​​ 예를 들면 15개의 명령을 처리하는데 걸리는 시간은 총 24 사이클을 필요로 하는 셈이다.

 

[그림 1] 10 단 파이프 라인을 가진 CPU의 명령의 흐름


 먼저 파이프 라인 스톨로, 이것은 파이프 라인이 "멈춘다"(Stall) 는 것을 나타낸다. 자주 있는 경향으로 Data Fetch 다. 이것은 요약하면 데이터를 넣는 처리이다.

C = A + B
 예를 들어, [명령 3]이 위의 명령 "A와 B를 가산하여 C 에 결과 넣는다"를 실행하면 가산에 앞서 A와 B의 값을 확정해야한다. 이 값을 저장하는 것에는 CPU 내부 레지스터가 할당되는 경우가 많으며,이 경우 지연이 나오지 않는다. 대개의 경우 내부 레지스터는 1 사이클에서 액세스 되기 때문이다.

 그런데 레지스터의 수는 많지 않고, 레지스터에 모든 데이터가 들어 있다고도 할 수 없다. 어디선가 메모리에 액세스 할 필요가 있다. 위 명령의 경우에는 "A는 레지스터를 사용할 수 있지만, B는 메모리에서 값을 얻을 필요가 있다" 고 가정하자. 그러면 어떻게 될까? 라고 하는 것이 다음의 [그림 2] 이다.

 

[그림 2] 그림 1을 처리하는 동안 [명령 3] 에서 메모리 액세스가 발생한 경우


 7 클럭까지 순조롭게 파이프 라인의 각 단계를 실행한다. 그런데 7 클럭 번째 Data Fetch 단계 (그림의 노란색 부분)에서 메모리 액세스가 발생하면 파이프 라인은 데이터를 메모리에서 로드 할 때까지 대기하게 된다. [그림 1]을 다시 봐주면 알겠지만 파이프 라인의 경우는 "어떤 스테이지가 대기하면 그 이후의 스테이지도 모두 대기 할 수밖에 없다"는 단점이 있다. 따라서 [명령 3] 이후 처리는 전부 메모리 액세스에서 돌아올 때까지 대기하게 된다.

 

문제는 "얼마나 기다려야 하나"이다. 일례로, 수중에 있던 Core i5-750 (2.66GHz)를 사용하여 광범위한 벤치 마크 소프트 "Sandra 2010"의 Memory Latency 테스트를 실행해 보았다.

 

 

Core i5-750 Memory Latency 테스트 순차 액세스의 경우 (빨간색 선이 테스트기)

 

 

또한 랜덤 액세스의 경우


 윗쪽의 순차 액세스의 경우 1차 캐시를 히트 범위는 3~4 사이클 2차 캐시면 10~20 사이클 정도에서 끝나지만, 메모리 액세스가 되면 50 사이클 이상 필요하게 된다. 아래쪽 랜덤 액세스의 경우, 이쪽에서는 더욱 심해지고 있는걸 알 수 있다. 특히 메모리 액세스는 200 사이클 이상 요하고 있다.

 즉, 아무것도 노력하지 않고 메모리 액세스가 발생해 버리면 [그림 2]처럼 막연히 파이프 라인을 멈추고 수백 사이클 대기 한다는 말도 안되는 상황에 빠지는 것을 알수있다. 이 경우 15 명령의 처리에 124 사이클 필요로 하는 것으로, [그림 1]의 이상적인 동작의 20%의 성능 밖에 낼 수 없는 것이다.


메모리와 CPU의 속도 차이를 메우는 캐시의 활용

 이것을 피하기 위해서는 어떻게 해야하나? 먼저 아이디어가 "캐시 사용"이다. 80286 당시 세대까지 CPU와 메모리의 속도에 그다지 큰 괴리가 없었다. 그런데 80386 세대 후반부터 점차 CPU 속도가 메모리의 속도를 대폭 상회하게 된다. 따라서 메모리 액세스만 끝 마치면 메모리 속도의 느림에 다리를 잡히게 됐다.

 그래서 CPU 측 또는 칩 세트 측에 캐시 메모리를 제공함으로써 속도의 차이를 메우는 방안이 취해지게 되었다. 그러나 캐시도 만능은 아니며 다이 크기를 낮게 억제라는 관점에서도 그렇고 캐시 메모리가 클수록 탑재하기 어렵다. 이를 보완하기 위해 캐시 계층 구조를 마련하거나 캐시 관리를 궁리하거나 캐시 구성 자체를 궁리하는 등 다양한 기법이 이용되게 되었다.

 또한 캐시를 얼마를 넣어도, 거기에 필요한 데이터가 들어 있지 않으면 의미가 없다. 이를 해결하기 위해 "프리 페치"(데이터 미리 읽기)라는 기법이 널리 사용되고 있다. 더 적극적으로 프리 페치를 실현하기 위해 "헬퍼 스레드" "Victim Thread" 등으로 불리는 다중 스레드 기법도 있다. 무엇보다 이것은 계속 연구가 계속되고 있는 것 같지만, 이것을 먼저 탑재 할 예정 이었던 것이 환상의 인텔 CPU "Tejas" 보여진것 처럼, 그 후에도 이를 적극적으로 이용한 예는 나타나지 않고 있다.


[고전 2001.08.30] 인텔 하이퍼 쓰레딩 펜티엄4 계획, 인텔 투기 (Spectulative) 스레드 실행

 

[분석정보] Intel 차세대 하이퍼 쓰레딩 (Hyper-Threading) 기술 공개


 더 적극적으로 "원래 데이터를 캐시나 레지스터에 넣을때 까지 파이프 라인에 명령어를 집어 올리지 않습니다" 라는 역 발상을 구현하는 프로세서도 존재하고 (x86은 아니지만) 이것은 "스톨 프리 파이프 라인" 등으로 불리고 있다. 또 다른 아이디어는 "메모리 액세스가 발생해도 파이프 라인을 멈추지 않는다" 라고 하는 구조가 있다. 이것은 "아웃 오브 오더 '로 구현되게 되었지만, 이것은 다른 시간에 설명 하기로 하자.

 

분기 명령에서 발생하는 파이프 라이닝 재처리
파이프 라인 해저드


 문제의 또 하나는 파이프 라인 해저드 (Pipeline Hazard)이다. Hazard에는 "위험, 우연, 운, 장애"등의 의미가 있지만, 여기서의 의미는 '사고'가 가장 적절할 것 같다. 그러면 어떤 사고가 있습니까? 라고 하는 것은, [그림 3]과 같은 케이스가 알기 쉽다. 이른바 "분기"가 등장하는 장면에서 어떤 값에 의해 처리를 나눠, 같은 케이스에 반드시 이용되는 것이다.

 

 

 

[그림 3] 작업중에 분기 명령이 나왔을 경우의 흐름도

 

 [그림 3]의 예에서는 명령 5-7 대신 명령 8-10을 처리하는 방향으로 분기를 가정하자. 이 경우 파이프 라인의 동작은 [그림 4]와 같이된다.

 

 

[그림 4] 과정에서 [명령 4] 조건 분기가 발생한 경우


 파이프 라인은 [명령 1]부터 점점 Fetch를 행하면서 순차적으로 처리해 간다. 명령 1 ~ 3은 문제 없다. 그런데 분기 처리를 하는 [명령 4] 대해서는 해당 분기 방향이 확정되는 것은 Execute가 끝나고 Writeback 단계에 들어서 된다. 사이클로 말해서 12 번째주기에서 간신히 "명령 4 후에 명령 8이 오지 않으면 안된다"는 것이 확정되는 것이다.

 그런데 파이프 라인은 이미 명령 5-7를 순차적으로 처리하고 있다. 따라서 13 번째주기에 파이프 라인 플러시가 발생한다. 즉 파이프 라인을 한번 새롭게 되돌리는 것이다. 그뒤, 14 사이클에서 다시 [명령 8] 이후의 Decode를 시작하는 형태가 된다. 이렇게 파이프 라인의 재처리가 발생 하는 것이 파이프 라인 해저드이다.

 이 파이프 라인 해저드 또한 한번 발생하면 큰폭으로 성능이 떨어지게 된다. 만약 파이프 라인 해저드가 전혀 일어나지 않고 처리 할 수​​ 있으면, [명령 1] ~ [명령 13]까지 총 10명령 이니까 (명령 5-7은 처리하지 않기 때문에) 이론적으로는 19주기 있으면 처리가 완료 할 것이다. 하지만 실제로는 28주기를 요하는 메모리 액세스로 인한 파이프 라인 스톨 정도의 영향은 아니지만, 동작 주파수가 30% 줄어든 것과 같은 정도로 성능이 저하 될 것이다.

 특히 이 파이프 라인 스톨의 경우, 파이프 라인이 길어질수록 성능에 미치는 영향이 커진다는 특징이 있으며, 성능을(클럭을) 끌어 올리기 위해 파이프 라인 단수를 늘리면 오히려 성능이 떨어질 수 밖에 없는 상황이 된다.

 

 

파이프 라인 해저드 대책에는 분기 예측 및 부분 플러시

 이를 보완하기 위한 방안으로 널리 이용되고 있는 것이, 분기 예측 메커니즘이다. 파이프 라인 해저드는 한마디로 "잘못된 방향의 명령을 해독하기 때문에 발생한다" 라고도 말할 수 있는 것으로, 제대로 명령을 해독하면 이러한 사태는 막을 수 있다. 그런데 디코딩시에 분기 명령을 발견하면, "다음 어느 방향으로 분기하는?"을 제대로 판단하고 다음 명령을 결정하는 구조가 지금의 CPU에 탑재되어 있다.

 또한 "부분 플러시" 라고 하는 장치를 탑재하고 있는 것도 있다. [그림 4]는 분기 미스가 발생하면 파이프 라인 전체를 플러시 하고 있었다. 그러나 [그림 4]의 경우 틀리다는 명령 5 ~ 7 까지의 처리로 명령 8 이후는 그대로 계속해도 문제 없다. 그래서 명령 5-7 처리하는 스테이지만 플러시 하는 것으로, 파이프 라인 해저드시 패널티를 완화 하자는 것이다.

 

 

[그림 5] 그림 4에 부분 플러시를 도입한 경우


 실제로 [그림 5]의 경우 패널티는 3주기 분으로 끝난다. 하지만 이기구는 단순한 파이프 라인에서 구현하기 쉽지만 다양한 테크닉을 구사하고 있는 작금의 파이프 라인은기구적으로 너무 복잡해 지기 때문에 구현되지 않을 수도 있다.

 이외에도 분기 미스가 발생하는 것을 전제로 발생되는 경우의 패널티를 완화하는 방법이 있다. Decode 에서 분기를 확인한 경우 그 분기를 2가지 모두 내부 테이블에 저장하여 두는 것이다. 이는 분기 미스가 발생했을 경우에도 즉시 다음의 디코딩을 시작할 수 있다.

 더 적극적인 방법으로는 "투기 실행"(Speculative execution 투기 실행의 eager execution) 이라는 구조를 구현하고 있는 CPU도 있다. 예를 들면 인텔의 Itanium이다. [그림 3] 에서는 [명령 4]의 한쪽만 실행하기 때문에, 빗나간 경우 패널티가 크다. 그렇다면 [명령 4] 후의 명령 5/6/7 과 명령 8/9/10 양쪽을 동시에 처리를 해두고, [명령 4]의 결과가 확정된 시점에서 잘못된 분기의 결과를 버리는, Partial Flush를 더 적극적으로 행 하는 구조다. 다만 이것은 CPU가 다소 복잡함이 과해져 채용 예는 거의 없다.


 파이프 라인의 문제와 그것을 커버하는 기술을 대충 복습 했으니, 다음에 다른 고속화 기법인 "슈퍼 스칼라" 에 대해 소개하고 싶다.

 

 

 

[분석정보] CPU 고속화의 기본 수단 파이프라인 처리의 기본 1/2

 

 

[분석정보] 슈퍼 스칼라에 의한 고속화와 x86의 문제점은

 

 

[분석정보] 명령의 실행 순서를 바꿔 고속화 하는 아웃 오브 오

 

 

[분석정보] x86을 고속화하는 조커기술 명령변환 구조

 

 

[분석정보] CPU와 메모리의 속도 차이를 해소하는 캐시의 기초지식

 

 

[분석정보] 캐쉬 구현 방식으로 보는 AMD와 인텔이 처한 상황

 

 

[고전 2005.11.30] 마이크로 아키텍처의 변화를 반영하는 "Core"브랜딩

 

 

[분석정보] 20년 후인 지금도 곳곳에서 살아남은 펜티엄 아키텍처

 

 

[분석정보] 5W 이하의 저전력 프로세서의 개발로 향하는 Intel

 

 

[분석정보] intel의 듀얼 코어 CPU 1번타자 Montecito

 

[분석정보] 멀티 코어 + 멀티 스레드 + 동적 스케줄링으로 향하는 IA-64

 

 

[고전 2002.10.04] Pentium 8 후보 Nehalem 아키텍처

 

 

[정보분석] Penryn의 1.5 배 CPU 코어를 가지는 차세대 CPU "Nehalem"

 

 

[고전 2002.09.12] Hyper-Threading Technology를 지원하는 HTT Pentium 4 3.06GHz