본문 바로가기

Lecture & Column/cpu_lec_col

AMD Zen 미리보기 : 현대 CPU의 구조 2015

Author : Daeguen Lee

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




안녕하세요 독자 여러분. 오랜만에 글다운 새 글을 들고 여러분을 찾아뵙게 되어 기쁜 글쓴이입니다. 이 글은 며칠 전 있었던 AMD의 주주총회와 거기에 뒤따라 개최된 Financial Analyst Day 행사에 영감을 받아 쓰여지게 되었는데, 혹시 이 날 어떤 일들이 있었는지 아시는지요. 바로 AMD의 새 아키텍처 -모듈 구조와 클러스터 멀티스레딩을 도입한 이후 만 5년만에 새로 선보일!- "Zen" 의 정보가 일부 공개된 것입니다. 현재까지 상세한 정보가 알려지지 않아 많은 부분을 추측에 의존해야 함에 불구하고, 보일듯 말듯한 정보를 바탕으로 모래성을 지어보는 것 역시 쏠쏠한 재미일 것이란 생각에 이렇게 만 다섯살 된 글을 리뉴얼할 결심을 하게 됐습니다. 아마 다음 분기쯤 인텔 6세대 코어 프로세서 (코드네임 스카이레이크) 가 출시되면 다시 한번 업데이트를 해야겠지만...^^;


각설하고, Zen에 관해 AMD가 공식적으로 언급한 내용을 요약하면 아래 이미지와 같습니다.



물론 AMD 자신의 자료이니만큼 다소 허풍이 섞여 있다고 가정하고, 적당히 색안경을 쓰고 바라봐줘야 할 필요가 있겠습니다만 역시 자사의 전/현세대 아키텍처인 불도저와 엑스카베이터를 함께 비교선상에 둔 것으로 보아, 어쨌든 불도저로부터 엑스카베이터까지의 성능향상폭을 모두 합친 것보다 더 큰 성능향상이 있는 것만은 확실해 보입니다. 과연 Zen은 구조적으로 어떤 특징을 가진 아키텍처인지 우선 알아보고, '알아낼 수 있던 것' 에 한해서라도 이에 근거한 몇몇 시나리오에서의 추정 성능을 알아보고자 하는 것이 이 글의 목표입니다.


자, 아래 그림은 참 오랫동안 우려먹고 있죠. 다들 익숙하신가요?



일반적인 CPU(와그 주변)의 구조는 위 그림과 같습니다.

더 자세한 설명에 들어가기 전에, 일반적인 CPU 구조와 AMD의 모듈 구조가 어떻게 다른지 간단히 짚어 보도록 하겠습니다.



1세대 및 2세대 불도저인 불도저 / 파일드라이버는 프론트엔드의 대부분을 한 모듈 내에서 공유합니다. 그 말인즉, '2코어' 불도저(파일드라이버)라 하더라도 프론트엔드 부분까지는 일반적인 1코어 CPU와 크게 다르지 않다는 뜻이기도 합니다. 다만 백엔드 직전, 비순차 실행유닛(=백엔드, Out-of-Order Execution Unit)에 명령어를 공급해주는 장치인 비순차 큐(Out-of-Order Queue)가 모듈 내에서 두 개로 복제되어 있음을 보실 수 있을 겁니다. 그리고 프론트엔드와 달리 백엔드는 본격적으로 '두 개의 코어' 처럼 부풀려져 있는데, 자세히 보면 그 중에서도 정수연산 유닛은 2코어 분량이 탑재되어 있지만 부동소수점 유닛은 여전히 모듈 내에서 공유되고 있는 구조입니다. L1 데이터 캐시 역시 개별 '코어'에 맞춰 두 개로 나뉘어 있습니다.


한편, 3세대 및 4세대 불도저인 스팀롤러 / 엑스카베이터는 위에 더해 '디코드' 단계 역시 두 개로 복제된 것이 특징입니다. 결과적으로 1/2세대가 2코어라기보다는 정수 유닛을 확장한 1코어에 가까웠다면, 3/4세대는 반대로 페치 단계와 부동소수점 유닛을 공유하는 2코어에 보다 가까워진 모양이 되었습니다. 외견상으로는, 그리고 이론적으로는 성능 역시 그만큼의 향상이 있었어야겠지만 실제로는 그렇지 않았는데, 이 글에서는 얕게나마 그 이유 역시 짚어 보도록 하겠습니다.


그럼 이제부터 본론으로 들어갑니다. AMD의 (현재로써는) 마지막 황금기를 누렸던 주인공, K8 아키텍처에서부터 시작합시다.

(K8과 K10(바르셀로나), K10.5(상하이/이스탄불)의 아키텍처는 큰 차이가 없어 이 글에서는 하나로 뭉뚱그려 설명합니다.)



이 구조는 사실 AMD K7부터 큰 변화가 없다고 봐도 무방한데, 그만큼 K7이 우수한 설계를 가지고 있었다고 보면 되겠습니다. 마침 비슷한 시기에 나왔던 인텔의 P6 역시 일부분만 개량되어 코어, 네할렘, 샌디브릿지, 하스웰에 이르기까지 적용되고 있으니, 그때 그 시기에 CPU 제조사들이 신내림이라도 받았던 게 아닌가 하는 생각도 듭니다.


K8/K10은 64KB의 L1 데이터/명령어 캐시를 탑재하고 있으며 (=도합 128KB) L1 명령어 캐시로부터 읽어드린 명령 스트림은 최대 32바이트까지 프론트엔드의 페치 버퍼에 저장됩니다. 제각기 다른 길이와 복잡도를 갖는 CISC 세트에 속하는 x86 명령어는 매 클럭 사이클당 3개씩 디코더로 보내지며, AMD의 디코더는 받아들인 명령어를 3종류로 분류해 CPU 내부에서 처리 가능한 RISC 형태로 변환합니다(=디코드). 3종류란 (1) 2개 이상의 매크로옵으로 변환되는 부류 / (2) 1개의 매크로옵으로 변환되는 부류 / (3) 1개의 마이크로옵으로 변환되는 부류인데, AMD의 '매크로옵'이라는 용어는 인텔의 그것과는 지칭하는 의미가 다른 점에 유의하셔야 합니다. (인텔의 매크로옵은 x86 명령어 자체를 의미하나, AMD의 매크로옵은 마이크로옵(내부 RISC 명령어) 중 일정한 규칙에 따라 '묶음' 취급이 가능한 것을 의미합니다.) AMD 디코더가 생성하는 일반적인 매크로옵은 산술 연산 명령어 마이크로옵과 메모리 접근 명령어 마이크로옵을 묶은 것입니다. 대체로 모든 연산은 수행된 후 결과값을 특정한 메모리 주소에 기록하는 것이 목적이기 때문에 이러한 묶음은 효율적으로 작동합니다.


3개의 디코더가 각각 사이클당 1개의 매크로옵(당연히, '묶을' 쌍이 없으면 1개의 마이크로옵 - 이하 본 글에서 내부 RISC 명령어는 모두 '마이크로옵'으로 지칭합니다)을 생성하면 생성된 마이크로옵들은 모두 AMD가 Instruction Control Unit이라 이름붙인 '재정렬 버퍼'에 들어갑니다. 재정렬 버퍼는 최대 72개의 마이크로옵을 저장할 수 있으며, 적절한 시점에 최대한의 병렬성을 확보해 명령어가 처리되게끔 백엔드에 보내 주는 역할을 하는 '작업 화면' 에 해당합니다. 고전게임인 테트리스를 해 보신 분은 아시겠지만 앞으로 등장할 블럭이 무엇인지 보여주는 창이 넓으면 넓을수록 더 좋은 전략을 세울 수 있을 것이고, 창이 없거나 매우 적을 수의 블럭만 보여준다면 플레이하기가 어려울 것입니다. 재정렬 버퍼의 크기가 바로 그 '창'의 크기를 의미한다고 생각하면 되겠습니다.


다음은 1/2세대 불도저의 프론트엔드 구조입니다.



앞서 간단히 설명했지만 다시 한번 자세히 살펴보도록 하죠. 우선 모듈 단위의 멀티스레딩 (클러스터 멀티스레딩) 을 수행하기 위해 L1 데이터 캐시가 두 개로 복제되어 있는데 각 캐시의 용량은 K10 대비 4분의 1수준으로 줄었습니다. 따라서 모듈=2코어 기준으로 합산하더라도 32KB로, 종전의 1코어보다 오히려 절반 수준으로 줄어든 캐시를 갖게 된 것이 특징입니다. 마찬가지로 페치 버퍼 역시 16바이트로 줄어들었으며 대신 디코더가 한개 더 늘어 사이클당 4개의 x86 명령어를 최대 4개까지의 내부 RISC 명령어 포맷 (마이크로옵) 으로 변환할 수 있게 되었습니다. 하지만 여전히 명심해야 할 것은 위 그림은 불도저 "모듈"을 설명하고 있다는 점입니다. 2코어당 4개의 디코더를 산술적으로 생각하면 1코어당 2개인 꼴이고, 종전 K10까지는 1코어당 3개의 디코더가 있었으니 아주 러프하게 보면 프론트엔드 성능이 3분의 2 수준으로 떨어졌다고도 생각할 수 있겠습니다.

 

또한, 재정렬 버퍼라는 이름 대신 디스패치 큐라는 이름이 등장했는데 이에 관해 짚고 넘어가도록 하겠습니다. (※ 2010년에 작성된 원 글에는 없던 내용입니다!)

 

인텔이 1995년 P5 마이크로아키텍처에 기반한 '펜티엄' CPU를 출시했을 때까지만 하더라도 프론트엔드에서 공급한 명령어를 백엔드에서 수행하는 방법은 단 하나, '들어온 순서대로' 연산하는것 뿐이었습니다. 이러한 방식을 In-Order라고 하며 가장 직관적인 순서에 의해 명령어가 처리되는 만큼 가장 단순한 하드웨어로도 구현이 가능하고, 그에 따라 소비전력 역시 이점이 있어 오늘날에도 저성능 모바일 CPU 아키텍처에는 간간히 재등장하곤 하는 개념이기도 합니다. (가까이는 베이트레일 이전의 아톰이나 밥캣 코어에 사용된 바 있습니다.) 하지만 여러 차원에서 '병렬성' 이라는 개념이 대두되고 CPU의 백엔드를 넓히는 방향으로 가게 되면서 순차 수행 방식의 한계가 드러나고, 이에 인텔은 P6 마이크로아키텍처부터 / AMD는 K5 아키텍처부터 비순차 수행(Out-of-Order Execution)을 구현하게 됩니다. 비순차 수행은 오늘날 CPU의 핵심이라고 해도 과언이 아닙니다.

 

이러한 비순차 수행을 가능하게 하는 것이 앞서 설명했던 재정렬 버퍼 등의 하드웨어입니다. 명령어가 '최대한의 병렬성'을 확보하여 '동시에 많이' 처리되게끔 '재정렬' 해 주는 것이 이 장치의 목적인 만큼 순차 수행 CPU에는 없는 장치이기도 하며, 또한 프로세서 설계 철학에 따라 얼마든지 다른 방법으로 이러한 역할이 구현되기도 합니다. 여기에서 지금 설명할 주제가 드러나게 됩니다. 바로 비순차 수행 CPU라고 해서 모두 같은 방식의 'OoOE 하드웨어'를 탑재하는 것은 아니기 때문입니다. 이는 크게 (1) 재정렬 버퍼 기반 디자인과 / (2) 물리 레지스터 파일 (Physical Register File, PRF) 기반 디자인으로 구분됩니다.

 

재정렬 버퍼 기반 디자인에 속하는 대표적인 것으로 인텔의 P6, 코어, 네할렘, 샌디브릿지, 하스웰을 들 수 있으며 K7부터 K10까지의 AMD CPU도 이 방식을 사용합니다. (AMD는 재정렬 버퍼를 자신만의 이름 (ICU : Instruction Control Unit) 으로 부르긴 합니다만 본질은 같습니다.) 반면 PRF 기반 디자인을 사용한 대표적인 아키텍처는 인텔의 넷버스트이고, 지금 설명하고 있는 불도저 (1/2/3/4세대 모두) 역시 같은 방식을 사용합니다. 일단 각 디자인을 대표하는 아키텍처들의 면면으로 보아 대강 감(전자에 비해 후자가 훨씬 긴 파이프라인 스테이지를 가지며, 따라서 '상대적으로' 재정렬 버퍼식보다 PRF식 설계에 더 적은 트랜지스터가 투입될 것이라는 느낌적 느낌)이 오셨으리라 생각합니다. 이 설명은 엄밀히 말하면 정확하지 않지만, 아주 러프하게 보아 OoOE를 구현하는 방식이 몇 세대 전으로(=펜티엄 4 수준으로) 후퇴되었다고 보면 틀리지 않을 것 같습니다.

 

정리하자면 모듈 구조의 도입, 그럼에도 불구하고 작아진/퇴화된 L1 데이터 캐시 / 페치 버퍼 / 디코더 / OoOE 하드웨어로 불도저 프론트엔드의 특성을 요약할 수 있겠습니다. 특히 디코더가 수행하는 '디코드' 스테이지는 성능에 상당한 영향을 주기 때문에 아무리 백엔드가 좋아진다고 하더라도 태생적으로 성능상 한계를 안고 있는 설계라 할 수 있겠습니다. 비슷한 사례로 과거 넷버스트 아키텍처가 이와 비슷한 설계를 취한 적이 있었고, 당시 인텔이 목표한 바는 (다른 모든 부문을 축소해서라도) 파이프라인을 짧은 단위로 분절해 (=깊게 파서) 개별 파이프라인에의 체류시간을 짧게 하는 것, 다시 말해 클럭을 올리는 것이었고 이는 실제 불도저가 추구하던 것과도 유사한 측면이 있습니다. 차이점이라면 인텔은 테하스가 100W TDP를 돌파할 것으로 예측되자 과감히 넷버스트를 폐기했다는 점과 AMD는 기어이 220W TDP에 달하는 5GHz CPU를 출시했다는 점 정도랄까요.

 

다음은 3/4세대 불도저, 스팀롤러와 엑스카베이터의 프론트엔드 구조입니다.


 

그림이 갑자기 커진 것 같지만 긴장하지 맙시다. 사실 앞서 살펴본 1/2세대 불도저와 비교해 구조적으로 달라진 부분은 디코더 뿐입니다. 모듈 내 2개의 코어가 공유해 사용하던 4개의 디코더는 3/4세대에 접어들면서 각 코어당 4개로 정확히 두 배가 되었습니다. 또한 L1 명령어 캐시 역시 50%가량 증가해 96KB가 되었으며 이로 인해 캐시 미스가 30% 정도 감소했다고 (AMD의 자료에 따르면) 합니다. 과거 넷버스트나 지금의 불도저나 깊은 파이프라인이라는 공통점이 있고 이러한 설계에서 캐시 미스는 파이프라인 버블을 유발해 성능에 치명적인 악영향을 줍니다. 전체적으로 앞서 1/2세대의 프론트엔드를 살펴보며 지적했던 구조적인 약점을 보완하는 방향으로 노선을 잡은 것 같고 바람직한 개선이 이뤄졌다고 평가할 수 있겠습니다만, (1) 여전히 L1 데이터 캐시와 페치 버퍼 용량은 작고 / (2) 보다 근원적으로, 물리적으로 2코어분을 설계하는 자원을 아껴 '모듈' 구조를 도입한 것이 불도저의 탄생 이유인데 거기에 다시 자원을 투입해 2코어분에 가까운 하드웨어를 장착하게 된 것이 무슨 의미가 있는 걸까요. 그리고 백엔드의 약점은 아직 짚어 보지도 않았습니다.

 

아참, 깜박하고 넘어갈 뻔 해는데 디스패치 큐가 25%가량 증가해 각 코어당 40개의 마이크로옵을 저장할 수 있게 되었습니다.

 

다음은 -드디어- 오늘의 주인공, Zen의 차례입니다. 설명을 보기 전에 유출된 슬라이드를 먼저 보시죠.

 

 

위 슬라이드는 Zen이 엑스카베이터와 비교했을 때 어떻게 달라지는지 간단히 보여주고 있습니다. 다이어그램으로 드러난 것 외의 여백을 글쓴이의 상상력을 동원해 채워야 하는 시점인데, 아래 그림을 보시며 얼마나 그럴듯한 가정을 세웠나 한번 따져 보시기 바랍니다. Zen의 프론트엔드 추정 구조입니다.

 

 

AMD가 밝힌 (정확히는 내부자로부터 유출된) 슬라이드에 따르면 Zen은 지난 4년간의 흑역사 모듈구조를 버리고 다시 전통적인 설계로 회귀한 것이 특징입니다. 여기서부터의 추정은, 구조적인 복고(復古)가 동반된 부분에서는 과거 K10의 전례를 참조했으며 그렇지 않은 부분에 대해서는 직전 아키텍처인 엑스카베이터의 예를 참조해 이뤄졌음을 밝혀 둡니다.

 

우선 프론트엔드 쪽에서 명확히 밝혀진 것은 'Zen은 코어당 4개의 디코더를 탑재하고 있다' 단 하나뿐입니다. 모듈 구조를 폐기했으니 (모듈 구조 도입에 따라 변화했던) OoOE 하드웨어의 크기, L1 데이터 캐시 등이 필연적으로 바뀌어야 하며 '불도저 철학'의 설계에서 벗어난 만큼 페치 버퍼나 OoOE 하드웨어의 종류 등도 (보수적으로 생각하자면 과거 K10과 비슷한 모습으로) 바뀌었으리란 추측이 가능합니다. 일단 페치 버퍼는 K10과 동일한 크기인 32바이트로 복원시키고, PRF식 OoOE 설계에서 도입되었던 코어당 40 엔트리 디스패치 큐를 재정렬 버퍼로 변경하며 그 용량은 종전 72 엔트리보다 상향된 96~128 엔트리 정도가 될 것으로 보았습니다.

 

한편, L1 캐시 용량을 결정함에 있어 가장 간편한 가정은 '원래대로', 즉 K10처럼 L1 명령어 캐시 64KB + L1 데이터 캐시 64KB가 되는 것이었는데, 두 가지 가능성이 더 있어 보였습니다.

 

(1) L1 명령어 캐시 용량을 현행(엑스카베이터)대로 유지하는 것 : 모듈 구조의 단점을 만회하기 위해 어쩔 수 없이 늘린 것이라 해도 그로부터 얻은 이득이 뚜렷했던 것을, 차기 아키텍처에서 과감히 잘라낼 수 있을까 하는 의문이 들었습니다. 이 경우 L1 명령어 캐시를 96KB로 유지하고, L1 데이터 캐시를 (현 엑스카베이터 1모듈=2코어의 L1 데이터 캐시 용량 합인) 32KB로 두거나 48KB 정도로 상향할 가능성에 대해 고려해 보았습니다. 불도저를 설계할 당시, 전통적인 L1 명령어 : L1 데이터 비율인 1:1을 벗어나 명령어 캐시에 큰 비중을 둔 데에도 나름의 이유가 있었으리라 (예컨대 미래의 워크로드는 갈수록 명령어스트림의 비중이 커진다고 여겼으리라) 믿는다면, 이 믿음을 곧장 뒤집는 것도 그리 쉬운 결정은 아닐 것입니다.

 

(2) Zen의 캐시 계층 구조는 AMD의 전통적인 "Strictly Exclusive" 정책에서 벗어나 인텔과 같은 "Strictly Inclusive" 방식으로 전환되었습니다. 이 방식의 특성상 상위 계층의 캐시 용량이 하위 계층에 비해 거대할 필요가 없으며, 그간 AMD의 상대적으로 큰 L1 캐시 용량은 상당부분 그들의 exclusive 정책에 의한 것임을 고려할 때 Zen에서는 과거처럼 큰 L1 캐시를 달 필요가 없다는 생각도 해볼 수 있습니다. 이 경우 현재의 하스웰처럼 L1 명령어 / L1 데이터 캐시 모두 32KB 정도로 줄이는 것도 가능한 일이라 보았습니다.

 

따라서 이 모든 가정을 고려하여, L1 명령어 캐시는 32~96KB의 범위 내에서, L1 데이터 캐시는 32~64KB 가운데에서 결정될 것이라 가정했습니다.

 

일단 확실한 것만 논하자면, 클럭이 같을 경우 비슷한 구조를 가진 K10보다 같은 시간 동안 33% 더 많은 x86 명령어를 디코드할 수 있다는 점입니다. 앞서 과거의 K7과 P6이 매우 비슷한 IPC를 가졌었단 얘기를 했는데 (애슬론과 카트마이, 카퍼마인의 엎치락뒤치락하는 관계를 생각해 보면...) 공교롭게도 인텔이 넷버스트 이후 P6을 재도입하며 '코어' 마이크로아키텍처에서 수정한 부분이 바로 디코더를 하나 더 늘린 것이며, 그 이래로 AMD가 인텔과 약 30% 가량의 IPC 격차를 따라잡은 적이 없다는 사실은 매우 의미심장합니다. 프론트엔드의 역할은 백엔드에 끊임없이 연산할 마이크로옵을 공급해 주는 것이며, 백엔드가 충분히 빠르다는 가정 하에 프론트엔드의 개선이 큰 폭의 IPC 향상으로 이어지는 것은 CPU사(史)에서 흔한 일이었습니다.

 

불도저의 경우 모듈 내의 연산유닛들을 2코어 명목으로 공유해 사용했기 때문에 '백엔드가 충분히 빠르다'는 전제를 충족하지 못할 가능성이 있었지만, 모듈 구조를 폐기한 Zen에서 프론트엔드의 강화는 어떤 결과를 가져올까요. 상당히 흥미있는 대목이 아닐 수 없습니다. (이런 흥미로운 대목을 이렇게밖에 이야기하지 못하는 제 역량을 탓해 주세요 ㅠㅠ)

 

이제부터는 백엔드 구조를 살펴보도록 하겠습니다. 글의 절반이 끝난 셈이니 잠시 읽던 걸 멈추고 쉬셔도 좋습니다.ㅎㅎ

 

.

.

.

 

...자, 화장실 가셨던 분들 다 돌아오셨나요? 쉬는 시간 끝났습니다~

아래는 K8/K10의 백엔드 구조입니다.

 

 

K8/K10의 프론트엔드를 거친 마이크로옵은 최대 72개까지 재정렬 버퍼에 저장되어 있다가 적당한 시점에 각 스케줄러로 배분됩니다. K10은 3개의 정수/메모리 스케줄러와 3개의 부동소수점 스케줄러를 갖고 있으며 각각의 정수/메모리 스케줄러에는 정수연산유닛 한개, 주소생성유닛 한개가 쌍으로 묶여 있습니다. 즉 프론트엔드로부터 끊김 없이 마이크로옵이 공급된다는 가정 하에 K10의 백엔드는 사이클당 최대 3개의 정수 스칼라 연산 명령어 (IOPS), 3개의 부동소수점 정수/스칼라 연산 명령어 (FLOPS) 또는 정수 벡터 연산 명령어, 3개의 메모리 접근 명령어를 처리할 수 있으며 부동소수점 정수/스칼라 연산과 정수 벡터 연산은 서로 경합하는 관계에 있습니다.

 

총 9개의 포트는 각기 전능(plenipotency)하지 않고 명확히 구분된 역할을 갖고 있기에 전체 대역폭을 다 채울 상황은 많지 않아 보입니다만 각종 연산 명령어와 메모리 접근 명령어는 쌍으로 올 때가 많기에 이러한 경우의 병목현상이 최소화되게끔 설계되어 있다는 점에서 대단히 실용적인 백엔드를 가졌다고 볼 수 있겠습니다. 실제로도 AGU를 다른 연산유닛과 쌍을 이루는 것으로 계산하면 실질적으로는 6 이슈 포트 백엔드로 볼 수 있는데, P6부터 샌디브릿지에 이르기까지 인텔의 백엔드 역시 6포트 너비였다는 점이 단순한 우연의 일치는 아닐 것입니다. 한편 3개의 부동소수점 스케줄러에는 각각 한개씩의 부동소수점 연산유닛 (FPU) 이 연결되어 있습니다.

 

다음으로 1/2세대 불도저 모듈의 백엔드 구조를 보겠습니다.

 

 

앞서 살펴본 것과 같은 부분을 꼽아보는 것이 더 빠를 만큼 많은 부분이 변했(=빨간색으로 표시한 것)습니다. 다시 한번 하나의 모듈은 2개의 코어에 해당한다는 점을 상기하고, 위 그림을 찬찬히 뜯어 보도록 합시다.

 

우선 각 코어당 할당된 32 엔트리 디스패치 큐는 저장된 마이크로옵의 종류를 분류해 정수 스칼라 연산 명령어나 메모리 접근 명령어의 경우 자신이 속한 코어의 정수/메모리 스케줄러에 전달합니다. 부동소수점 연산, 정수 벡터 연산 명령어는 모듈 내에서 공유되는 부동소수점 스케줄러의 몫입니다. 이렇게 명령어를 받은 각 스케줄러는 다시 각각 4개의 이슈 포트에 연결된 하위 연산유닛에 마이크로옵을 내려보냅니다.

 

보시는 바와 같이 1개분의 코어가 사용할 수 있는 유닛은 정수 연산 유닛 두 개와 메모리 접근 유닛 두 개, 그리고 다른 코어와 경합해 가며 사용할 수 있는 부동소수점 연산 유닛 두 개, 정수 벡터 연산 유닛 두 개입니다. 이 수치만으로도 K10 대비 1코어당 사용가능한 자원은 거의 3분의 2꼴로 줄어들었음을 알 수 있는데, 설상가상 부동소수점 유닛들은 다른 코어와 공유하는 것입니다. 결국 K10 코어 2개와 불도저 모듈 1개를 비교하자면 정수 연산 대역폭은 3분의 2수준, 부동소수점 연산 대역폭은 3분의 1수준에 그치는 셈입니다. 물론 부동소수점 포트 자체는 4개로 K10의 3개 대비 33% 증가한 수치이며 (과거엔 부동소수점 연산과 정수 벡터 연산이 서로 경합하는 관계여서 어떻게 해도 이들 마이크로옵을 사이클당 3개 이상 처리하는 것이 불가능했지만, 불도저로 넘어오면서 FPU와 정수 벡터 유닛이 서로 다른 포트를 쓰게 되어 최적의 코드 하에서는 사이클당 4 마이크로옵의 처리도 가능해졌습니다.) 정수 벡터 유닛 중 한 개는 부동소수점 메모리 접근 전용으로 사용되기도 하며 실제로 K10의 3분의 1까지 성능이 추락하는 것만은 면했습니다.

 

그러나 불도저의 FPU나 정수 벡터 유닛이, 이러한 포트 배분을 '예견하고' 역할을 분리한 것이라 보긴 어렵습니다. 그보다는 불도저의 설계 철학(파이프라인 분절에 투입되는 것 이외의 모든 것을 간소하게)에 따라 K10의 개별 FPU를 경량화한 결과물이 그것이고, 포트를 분리한 것은 (그렇잖아도 경량화한 FPU를) 2개의 포트에 밀어넣을 경우 너무 처참해질 부동소수점 성능을 염려한 최소한의 조치로 받아들여집니다.

 

이렇듯 1/2세대 불도저는, 프론트엔드 측면에서도 K10 대비 3분의 2수준의 자원을 갖고 출발했으며 (디코더 3개 -> (코어당) 2개) 백엔드 역시 K10 대비 "최대" (최소가 아닙니다) 3분의 2 수준에 머물고 있습니다. 종합적인 성능 역시 같은 클럭에 같은 코어 갯수라면 K10보다 대부분 뒤떨어지는 성능을 보여 출시 당시로부터 현재까지도 많은 유저들에게 질타받고 있는 상황입니다. 이를 타개하기 위해 AMD는 프론트엔드를 개선한 3세대 불도저 스팀롤러를 작년 초 출시한 바 있습니다. 앞서 스팀롤러의 프론트엔드를 살펴보며, '이토록 개선되었는데 성능이 왜 안 올랐을까' 에 대해 고찰해 보자는 숙제를 남겨둔 바 있었고, 지금 그 숙제를 해야 할 것 같습니다. 아래는 3/4세대 불도저의 백엔드 구조입니다.

 

 

스팀롤러는 파일드라이버와 비교해서 프론트엔드가 상대적으로 많이 개량된 반면 백엔드의 변경은 극히 소폭에 그치고 있습니다. 디스패치 큐의 용량이 각각 32 엔트리에서 40 엔트리로 25% 증가했고, 정수/메모리 스케줄러의 용량 역시 40 엔트리에서 48 엔트리로 20% 증가했으며, 그림에는 드러나지 않았지만 FPU들 / 정수 벡터 유닛들 간의 역할 분담을 재조정해 더 높은 효율을 낼 수 있도록 했다고 합니다. (아마 FSTORE 전담유닛제를 폐지하고 모든 FPU가 메모리 계층에 접근할 수 있게 한 것이 아닌가 추측됩니다.) 한편 4개까지 있어봐야 별 필요도 없던 (...) 부동소수점 스케줄러의 네 번째 포트를 폐지하고, 대신 유닛 자체는 4개를 모두 남김으로써 3 포트에 선점적(pre-emtive)으로 4 유닛 중 3개를 배정하는 조치를 취했는데 이는 트랜지스터 수와 면적을 줄이기 위한 조치였다고 합니다. 포트를 줄였음에도 불구하고 각 유닛간의 밸런스 최적화 등으로 인해 (그리고 프론트엔드의 개선에도 힘입어) 스팀롤러의 부동소수점 성능 자체는 동 클럭의 파일드라이버를 10+a% 가량 안정적인 갭으로 상회하게 되었습니다.

 

그러나, 스팀롤러의 성능이 크게 향상되었다고 말하면 십중팔구는 그대로 믿지 않을 것입니다. 그 이유는 크게 두 가지입니다.

 

(1) 1/2세대 불도저의 성능이 너무 낮았다 (...) : 실제로 스팀롤러 자체는 동 클럭의 파일드라이버보다 10~20% 가량 오른 성능을 보여줍니다. 문제는 '동 클럭'을 달성하기 어려워져서 같은 급의 SKU라면 파일드라이버보다 항상 10% 정도 낮은 클럭을 갖게 된 점인데, 이 때문에 결과적으로는 거의 성능향상이 없게 되었단 점과 앞선 세대의 불도저가 너무 성능이 낮았던 탓에 조금 오른 정도로는 성능향상의 '티' 가 나지 않았다는 점이 스팀롤러를 위한 변명이 되지 않을까 합니다.

 

(2) 프론트엔드 보강이 효과를 보기엔 백엔드가 너무 빈약했다 : 앞에서 수차례 언급했듯 프론트엔드는 백엔드에 마이크로옵을 공급해 주는 기능을 수행합니다. 다시 말해 프론트엔드의 성능은 높은 성능 발휘를 위한 필요조건이지 충분조건은 아니란 뜻인데, 어디까지나 연산을 수행하는 것은 백엔드란 점을 잊어서는 안 되겠습니다. 물론 백엔드의 성능이 너무나 좋아 대부분의 마이크로옵 공급량을 빠른 시간 내에 처리해 버리고 대부분의 시간을 놀고 있는 상황이라면 프론트엔드의 확장이 큰 성능향상을 가져다 주겠지만 불도저의 백엔드가 과연 그러한 상황이었는지 냉정히 따져봐야겠죠.

 

1/2세대 불도저는 모듈당 4개의 디코더를 가지고 있었고 3세대 들어 이것을 '코어당' 4개로 두 배 늘렸습니다. (프론트엔드 파트에서 언급했듯 L1 명령어 캐시 용량도 50% 늘렸지만 여기서는 따로 다루지 않겠습니다.) K10과 비교하더라도 33%가 더 많습니다. 반면 코어당 백엔드 유닛 수는 K10 대비 3분의 2수준으로 감소한 상황인데, 아주 러프하게 디코더 하나가 먹여살려야(=feeding) 하는 백엔드 유닛 수를 따져 보면 K10이 9/3=3개, 1/2세대 불도저가 6/2=3개, 3/4세대 불도저가 6/4=1.5개가 됩니다. 디코더가 1개의 x86 명령어를 디코드한 결과물이 2 마이크로옵의 결합체인 '매크로옵' 임을 고려하면 앞의 비율은 2에 근접하게 되는 것이 바람직한데, 스팀롤러는 이미 그보다 낮은 값을 갖고 있습니다. 다시 말해 디코더 증가분이 별로 효과를 얻지 못할 상황인 것입니다.

 

한가지 흥미로운 점은 스팀롤러와 정 반대의 노선을 걸은 것이 바로 인텔의 하스웰이란 점입니다. 상대적으로 약체인 백엔드를 두고 프론트엔드를 강화한 스팀롤러와 달리 하스웰은 수년간 변함없이 사용되어 온 프론트엔드를 유지한 채 백엔드 포트 수를 33% 늘렸습니다(6개 -> 8개). P6 기반 아키텍처의 (그리고 큰 틀에서, K7 기반 아키텍처의) 프론트엔드 : 백엔드 구성이 일종의 황금비처럼 여겨지는 가운데 하스웰 역시 큰 성능향상을 얻지 못하며 이러한 가정을 한층 강화시킨 바 있습니다. 그러나 이는 더 큰 가능성을 시사하는 것이기도 합니다. 현 단계에서는 백엔드를 늘림으로써 '황금비'에서 이탈하게 되었지만, 다음 단계에서 프론트엔드를 늘려 다시 황금비를 달성한다면...? 실제로 스카이레이크는 인텔이 코어 마이크로아키텍처 이래 9년간 고수해 온 4 디코더 프론트엔드를 대폭 확장할 것으로 알려져 있고, 스팀롤러의 경우와 달리 든든한 백엔드가 뒷받침되는 프론트엔드의 개선은 곧 큰 성능향상으로 이어지리란 가정도 가능합니다.

 

애초 AMD가 모듈 구조를 도입하며 의도한 모든 과녁(높은 효율, 높은 멀티스레드 성능, 낮은 소비전력)이 빗나가버린 가운데 '불도저 개량 로드맵' 으로 이미 4년 전부터 제시되었던 파일드라이버 / 스팀롤러 / 엑스카베이터의 투입 모두 결국 인상적인 성능향상을 가져오는 데에는 실패했다고 봐야겠습니다. (사실 성능향상폭 자체는 불도저 -> 파일드라이버에서 10% 가량, 파일드라이버 -> 스팀롤러에서 10+a% 가량으로 꽤 견실한 편이었습니다. 같은 기간 샌디브릿지 -> 하스웰에 이르는 성능 정체를 감안하자면 더더욱 그렇죠.) 4년 연속 / 4개 아키텍처 연속으로 쓴맛을 본 AMD의 마지막 희망이 Zen에 걸린 셈입니다. 바로 오늘의 주인공, Zen의 백엔드 구조를 보겠습니다.

 

 

앞서 Zen의 프론트엔드 파트에서 다루었듯 모든 예측은 '전례'에 기반해 최대한 보수적으로 잡는 것을 원칙으로 했습니다. 유출된 다이어그램 상에는 정수 파이프라인이 6개로 표시되어 있고, 이를 장미빛으로 아전인수하자면 인텔식의 "6포트 정수유닛" 으로 받아들일 수도 있겠으나 최대한 AMD의 전례를 따라 3 정수 연산 유닛 + 3 메모리 접근 유닛으로 가정한 것 역시 그러한 마음가짐의 일환입니다.

 

위에서 스팀롤러의 '오버', 그리고 하스웰의 '준비'를 대비해 이야기를 풀어 보았는데, 여기서 얻을 수 있는 교훈은 (1) 황금비를 준수해야 한다 (괜히 P6 / K7이 장수한 게 아닙니다) / (2) Zen이 K10보다 늘어난 디코더를 갖는 만큼 백엔드의 확장도 동시에 이뤄져야 성능향상을 극대화할 수 있으리라는 점입니다. 다만 아직까지 밝혀진 바에 따르면 AVX 부동소수점 연산을 하는 경우가 아니면 종전 K8/K10, 인텔 코어~샌디브릿지 수준의 백엔드 대역폭 이상을 기대할 수는 없을 것 같습니다.

 

여기서도 "확실히 드러난 것" 위주로 언급하자면 정수 포트가 6개가 되었다는 점, 각 스케줄러가 K10처럼 세분화되지 않고 불도저처럼 'Unified' 형태로 존재한다는 점, 그리고 FMAC 스트림 폭이 128bit에서 256bit로 확장되었다는 점입니다. 과거 불도저가 128bit FMAC 유닛 두 개로 256bit AVX 명령어를 사이클당 한 개 비율로 처리할 수 있었던 점을 고려할 때 (=AMD FlexFP) Zen 역시 512bit AVX2 명령어를 2개의 256bit FMAC를 통해 사이클당 한 개 비율로 처리할 수 있으리라 생각됩니다.

 

다만 부동소수점 연산 유닛에 배정된 포트 자체는 K10보다 33% 줄어든 두 개에 그치고 있어, AVX가 아닌 x87 / 128bit SSE 명령어의 경우 K10 대비 67%에 그치는 스루풋을 보일 것으로 예상됩니다. 이 부분을 보다 실감나게(?) 알아보기 위해 간단한 계산값을 준비했습니다 : 아래의 표들을 보시죠.

 

 

위의 표는 각 CPU별로 클럭, 코어 갯수, (코어당) 백엔드 정수 연산 유닛 갯수를 곱한 것입니다. Zen을 제외한 나머지 셋은 현존하는 SKU로 각각 스팀롤러, 파일드라이버, K10을 대표하는 4코어 제품들이며 Zen의 클럭은 파일드라이버 기반 SKU와 동등한 수준이 될 것이라 가정했습니다. 결과를 간단히 분석하자면 모든 코어를 사용할 때 스팀롤러보다 최대 50% 더 높은 정수 연산 성능을 보일 수 있으리라는 것인데... 너무 뻔하죠? ^^;

 

 

위의 세 개의 표는 각 명령어 세트별 부동소수점 연산 성능을 계산한 것입니다. x87 / 128bit SSE 연산성능은 정확히 FPU 포트 갯수에 비례해 계산되었고, AVX의 경우 불도저 계열의 성능이 최악으로 떨어지는 것을 볼 수 있습니다. FlexFP는 128bit FMAC 두 개로 AVX 명령어를 처리하게 해줄 따름이지 '각 FMAC 유닛마다 AVX를 처리' 한다거나 'AVX를 두 배 빠르게' 처리하는 기술이 아니기 때문입니다. 달리 말하자면 불도저의 FPU에는 AVX 유닛이 단 한 포트 열려 있는 것과 마찬가지이고, 그러한 약점이 위의 수치로 드러난 것이라 볼 수 있습니다. 반면 Zen은 256bit FMAC 포트를 두 개 가지고 있으며 각각의 코어가 독립적으로 온전한 FPU를 보유하고 있어 재차 불도저보다 강점을 가집니다.

 

 

위의 표는 AVX와 FMA (Fused Multiply and Add) 최적화 시나리오를 가정해 본 것입니다. 현실적으로 완벽히 이상적인 위와 같은 결과는 보기 어렵겠지만,

 

 

...위와 같은 '일부 최적화' 결과는 상대적으로 흔히 볼 수 있으리라 생각합니다. (위 두 표 중 위의 표는 AVX와 FMA에 각각 20%씩 최적화된 워크로드, 아래는 50%씩 최적화된 워크로드 가정입니다.)

특히 20% 최적화 시나리오는 현실적으로 비슷한 성능이 나타나리라 기대해봄직한 것이기도 한데, 바로 <하스웰의 모든 것 : 아키텍처편> 에서 아래와 같은 결과를 얻은 적이 있기 때문입니다.

 

 

네할렘 대비 16%가량 높은 샌디브릿지의 성능, 샌디브릿지 대비 5%가량 높은 아이비브릿지의 성능, 아이비브릿지 대비 8%가량 높은 하스웰의 성능...

그럴듯하지 않나요?

 

지금까지 살펴본 시나리오를 바탕으로, 가장 보수적이고 '그럴듯' 하다고 여겨지는 '20%-20% 최적화 시나리오'에 근거해 다른 표를 하나 만들어 보았습니다.

바로 최상위 Zen이 취할 것으로 여겨지는 8코어 SKU에 대한 가정입니다.

 


스팀롤러와 Zen의 8코어 SKU는 각각 FX-8370의 클럭에 준하는 것으로 가정하고 대입해 본 것입니다. Zen은 3코어 로드까지는 페넘 II X6 1100보다 다소 떨어지는 (근소한 차이이지만) 성능을 보이지만 4코어부터는 역전하기 시작하고, 무엇보다 불도저들과 다른 '네이티브 8코어' 라는 점에서 코어 갯수가 늘어날수록 견실한 성능 향상을 보일 것으로 예상되었습니다. 최종적으로는 페넘 II X6 1100보다 36%가량 향상된 멀티코어 성능을 보이리란 추측입니다.

 

예측이 다소 보수적으로 이뤄졌기 때문에 'Zen의 성능이 이것밖에 안 돼?' 하는 생각이 드신 분들도 많으시겠지만, 일단은 이렇게 접근해 보는 어프로치 자체가 재미있는 것 아닐까, 거기에 이 글의 존재가치가 있는 것 아닌가 하는 생각을 해 보았습니다. 개인적으로 만 4~5년만에 글을 다시 쓰려니 머리에 쥐가 나는 것 같기도 했고 한편으로는 무척 재미있었습니다. 읽고 계신 독자 여러분께도 이 글이 재미있게 읽혔길 바랍니다.

 

긴 글 읽어주셔서 감사합니다 :)

 

//

 

아래 '밀어주기'는 티스토리의 크라우드펀딩 시스템입니다. 100원부터 3000원까지의 범위 내에서 글쓴이에게 소액 기부가 가능합니다. 제가 작성한 글이 후원할만한 가치가 있다고 여기신다면 밀어주기를 통한 후원을 부탁드립니다. 물론 글을 '가치있게' 쓰는 것은 저의 몫이며, 설령 제 글이 후원할 만큼 가치있게 여겨지지는 못하더라도 그것이 독자 여러분의 잘못이 아니란 건 너무 당연해 굳이 언급할 필요도 없겠습니다. 저는 후원 여부와 관계없이 제 글을 읽어주시는 모든 독자분께 감사합니다.