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

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

tware 2010. 10. 4. 20:00

 

x86에 한해서, 아웃 오브 오더에서 빼놓을 수 없는 것이 명령 변환의 구조이다. 인텔의 경우 "μOp"(마이크로 옵) AMD (가 인수한 NexGen)는 당초 "RISC86" 라고 칭하고, 그 후 "Op"궁극적으로 "microOp" 라고 표기는 바뀌었지만, 이를테면 x86 명령을 "RISC 형" 내부 명령으로 변환하는 방법이다. 이 내부 명령이 μOp 라든지 microOp 등으로 불리는 것이다.

 여기서 잠깐 RISC와 CISC의 이야기를 하기로 한다. 예를 들어 x86라는 명령은 CISC의 대표적 예 이지만, 원래 RISC와 CISC의 차이? 라는 것을 간단하게 설명하자.


RISC와 CISC의 차이를 대충 복습

 RISC 개념을 그것을 명확하게 알지 못하고 탑재한 CPU는 꽤 예전부터 있다※1. 하지만 RISC 개념이 명확하게 된 것은 1981 년 데이비드 패터슨 (David Patterson)이 프로젝트의 지휘를했던 "RISC-I/RISC-II" 프로젝트와 거의 같은 1982년에 존 헤네시 (John Hennessy)가 프로젝트의 지휘를 했던 "MIPS"프로젝트가 각각 큰 성과를 올리고 이를 바탕으로 한 제품이 등장하게 되고 나서가 아닐까 한다. (IBM 801 라든지도 있지만, 여기에서는 생략한다).

※ 1964 년 "CDC6600 '가 처음이라는 설이 뿌리 깊다.

 여기서 처음으로, RISC (Reduced Instruction Set Computer : 축소(감소) 명령 세트 컴퓨터)라는 개념이 정의되었다. 이 당시의 RISC의 개념은 다음의 요소로 대표되고있다.

명령 길이는 고정
처리 시간은 명령에 의하지 않고 일정 (원칙 1 클럭)
연산은 레지스터만 해당
마이크로 코드를 폐하기
범용 레지스터를 다수 준비
지연 슬롯을 마련하고 파이프 라인 스톨 방지


 이들을 (전부는 아닐지라도) 탑재한 것이 RISC, 그렇지 않은 것이 CISC (Complex Instruction Set Computer : 복합(복잡) 명령어 세트 컴퓨터)라고 하는 것이 다소 크게 현실적인 분류이다. 예를 들어 CISC의 대표 예인 x86의 경우는 다음과 같다.

1. 명령 길이는 가변. 게다가 규칙성이 없기 때문에 명령을 1byte 씩 디코딩하지 않으면 최종 명령 길이가 불명
2. 처리 시간은 명령에 따라 크게 변화
3. 연산은 레지스터 간 연산 외에 메모리와 즉각적인 연산, 주소 지정 등 다양한 형태가 있다
4. 마이크로 코드를 많이 쓰는데 특히 80386는 디코딩 지연 시간이 꽤 많았다. (486부터 마이크로 코드 사용을 축소)
범용 레지스터는 매우 적고, 스택을 많이 사용할 필요가 있었다
5. 지연 슬롯에 상응하는 것은 없다

 

 

RISC의 장점과 단점

 단적으로 말하면, RISC는 "파이프 라인을 고속으로 동작하게 하는"것을 주목적으로 한 디자인이다. 이 목적을 감안할 때, 가변 명령 길이가 디코딩에 시간이 걸리므로 고정 명령어 길이 쪽이 유리하다. 또한 연산 대상이 메모리이거나, 간접 주소이거나 다른 것은 처리시 여분의 시간이 걸리는 것이 되므로, 연산 대상은 모두 레지스터로만 제한했다. 물론, 그럼 레지스터 밖에 닿지 않기 때문에 메모리와 레지스터 간의 로드 / 스토어 명령어는 따로 준비한다.

 마이크로 코드를 폐기하는 것도 같은 이유다. 마이크로 코드를 경유에는 디코딩에 시간이 걸리므로, 디코더는 모든 하드 와이어로 구성되어 있다. 레지스터 간 연산에만 한정하면, 당연히 필요한 레지스터 값이 증가하므로, 이것에 맞추어 사용할 수 있는 레지스터를 크게 늘린다.

 또한 로드 / 스토어 명령어는 당연히 메모리 액세스가 발생하기 때문에 처리가 늦어진다. 이것을 일반적으로 파이프 라인 처리로 행하면 파이프 라인 스톨이 발생해 버리므로 이를 피하기 위해 지연 슬롯을 준비. 처리에 시간이 걸리는 명령을 별도로 진행시켜 파이프 라인의 움직임을 저해하지 않도록 했다.

 적어도 1980 년대의 반도체 제조 기술에서는 이 계획은 대단했다. RISC 아키텍처를 채용한 프로세서로는 MIPS의 "R2000/R3000" 나 Sun의 "SPARC" AMD의 "Am29000 " 와 인텔의 '인텔 960'등 다방면에 걸쳐 모두 그만한 성능을 실현하는데 성공했다.

 그 이유는 여러가지 있지만, 역시 복잡한 x86 명령어를 그대로의 형태로 빠르게 디코딩하고 실행하는 것은 무리가 있었다,라고 하는 결론이 된다. 물론 RISC 계에도 단점이 있으며, 그것으로 (처리 해야 할) 명령 수가 늘어나는 추세인 것이다. x86 더하기 명령을 예로 보자.

ADD r/m32, r32
 이것은 "레지스터 (r) 와 메모리 (m)의 값 (왼쪽 인수 1)"과 레지스터 (오른쪽 인수 2)를 가산 그 결과를 첫번째 인수의 장소에 되 돌린다는 것이다. 이 명령어에서 메모리 액세스 하는 것과 동일한 것을 RISC 계열로 하려고 하면 다음과 같다.

load r001, Mem
add r003, r001, r002
store r003, Mem

 

 먼저 메모리의 값을 r001에 로드한다. 그런 다음 "r003 = r001 + r002"라는 레지스터 간 덧셈을 수행하고 마지막으로 r003의 결과를 방금 메모리의 위치에 되 돌린다는 것이다. 즉 동일한 작업을 하는데, RISC 계라면 3배의 명령을 실행하지 않으면 안되는  경우도 있다.

 또한 초기의 RISC 프로세서의 경우 1클럭으로 연산을 수행 할 수 있도록 "복잡한 연산을 구현하지 않는다"  처럼 해서, 정수의 곱셈조차 없었던 것도 있다 (소프트웨어로 구현하게된다.) 그러한 단점을 막을 정도로 더 과하게 있는 정도(당시로서는) 고속으로 동작한 것이 RISC 였다.  (모든 명령을 1클럭으로 실행하는 비법!! 1클럭에 하지 못하는건 안해버린다..;;;;;;;;;;)

 

[분석정보] 고속화를 가져오는 Radix-16 Divider와 shuffle Engine

 

 

[분석정보] IDF 2007 Penryn 벤치마킹 세션 리포트

 

 

[분석정보] AMD가 발표한 메인 스트림 APU Llano의 아키텍처

 

x86에서 μOp 변환의 구조

 그런데 이야기를 되돌리자. RISC의 개념은 슈퍼 스칼라와 아웃 오브 오더와 매우 친 화성이 높았다. 슈퍼 스칼라이든 아웃 오브 오더 쪽이든, 키가 되는 것은 "명령의 의존성을 어떻게 줄일 것인가?" 에 있다. x86을 그대로 처리하는 것은 종속성 해소에 상당한 시간이 걸리는 것은 분명 이를 해결하려면 "한번 x86을 RISC 형 명령으로 분해하여 이 명령을 슈퍼 스칼라 / 아웃 오브 오더 실행 하면 된다 "라는 것이다.

 명령의 분해는 바로 전 페이지에(위에 표시된) 표시된 RISC에서 가산 처리 방식이다. 즉, 메모리 액세스 등을 동반하는 명령어는 레지스터에 로드 명령과 그것을 사용한 연산의 두 가지로 분해하여 각각을 개별적으로 실행한다. 그러면 의존성 해소가 x86 명령의 상태에 비해 쉽게된다. 또한 레지스터 리 네이밍 등을 효과적으로 사용할 수 있게 된다는 것이다.

 참고로, 슈퍼 스칼라 / 아웃 오브 오더에 관해서는 명령 변환이 거의 필수적이다. 예외는 예전 Cyrix의 "MI" 에서 이것은 인 오더로 슈퍼 스칼라를 구현했다. 그러나 역시 의존성 해소 등이 곤란하고, 이것에 있어 당시의 Cyrix는 "슈퍼 스칼라는 2 명령어가 성능의 한계로 3 명령 이상해도 효과가 없다"고 주장했을 정도다. (인텔은 인오더 수퍼스칼라를 펜티엄에서 구현)

 이러한 x86 명령 및 RISC 명령어의 변환은 일반적으로 Decode 단계에서 구현된다. 그림 1과 같이 명령 1차 캐시는 어디 까지나 x86 명령어로 유지되어 Fetch도 x86 명령을 그대로 실시한다. 이것을 Decode 해석하는 대로 처음 μOp로 변환된다. 이후는 스케줄러를 거쳐 ALU 나름 Load / Store에 투입된다.

 

 

(명령 1차 캐시)  [그림 1] x86에서 μOp로 변환 흐름 (데이터 1차 캐시)


 덧붙여서, μOp로 호칭은 말로는 같아도, 그 내용은 당연히 아키텍처마다 다르다. 예를 들어 인텔의 경우 "Pentium Pro"(1995년 출시) 최초로 이를 구현한 다음"Pentium II / III'으로 진화하지만 이러한 CPU에는 섬세한 차이가 있다 ※ 1 , μOp에도 당연히 반영되는 것이다.

※ 1 Pentium II는 16bit 명령어의 취급이나 MMX 명령어, Pentium III에서는 SSE 명령어가 추가되어 있다.

 그 다음에 나온 "Pentium 4"는, 당연히 전혀 다른 μOp의 구성으로 되어 있다. 게다가 동일한 Pentium 4에도 Willamette / Northwood와 Prescott / CederMill ( 기사 )는 내부 구조가 완전히 다르기 때문에, μOp도 당연히 변한다고 생각된다. 하지만 x86 명령은 1 ~ 2 개 μOp로 변환되며 비교적 간단한 x86 명령은 거의 1 개의 μOp에 할당되는 특징은 인텔뿐만 아니라 AMD의 K6 ~ Phenom II 세대에서도 공통이다.


μOp의 진화한 활용법
MicroOps Fusion과 Macro Fusion


 참고로,이 μOp 잡는 방법을 일보 진화시킨 것이 인텔이 Pentium M에서 구현한 'MicroOps Fusion" 와 Core 2 (콘로. 코어 마이크로 아키텍처)에서 구현한 "Macro Fusion" 이다. 예를 들면 다음 추가 명령을 예로 생각한다.

ADD reg, mem


 이것은 mem에서 지정한 주소의 메모리에서 데이터를 읽어 들여, reg로 나타내는 레지스터 값과 가산하는 것이다. 이것을 보통으로 처리 할 경우 그림 2와 같이된다.

 

 

[그림 2] 더하기 연산의 분해 및 처리 흐름


 원래의 ADD 명령은 Fetch를 거쳐 Decode에 로드 되고 이것이 "ld"(load)와​​ "add"두 μOp로 분해된다. 이것이 그대로 스케줄러에 저장된 후 각각 ALU 및 Load / Store에 전달되어 실행된다. 그런데 x86의 경우 이러한 "ld + 무엇인가" 라는 조합이 아주 많다.

 그래서 이런 번잡하게 나오는 조합은 전용 μOp를 준비 스케줄러 단 까지 하나의 μOp로 인식하도록 했다 (그림 3). 이렇게 하면 스케쥴러는 많은 명령을 저장할 수 있게 되고, Decode에서 스케줄러까지 하나의 μOp로 처리되기 때문에 데이터 이동량이 줄고 결과적으로 전력 절감도 가능하게 된다. 또한 결과적으로 Decode 단에서 동시에 처리 할 수 ​​있는 x86 명령을 늘리는 효과도 있기 때문에 성능 향상에도 도움이 된다는 것이 MicroOps Fusion의 효과이다.

 

 

[그림 3] MicroOps Fusion의 예. "ADD"를 "ld + add"

라는 하나의 μOp로 변환 스케줄러에서 실행 유닛에 전달할 때 분할

 

이를 더욱 적극적으로 진행한 것이 Macro Fusion이다. 이쪽은 복수의 x86 명령어를 하나의 μOp에 넣는다는 것이다. 당초 대상이 된 것은, cmp / test 명령과 jcc 명령어의 조합이다. cmp / test 라는 것은 값을 비교하는 명령으로,이 결과는 "EFLAGS" 상태를 저장하는 레지스터에 반영된다.

cmp reg1, reg2


 위의 명령어는 reg1과 reg2 값을 비교하는 것으로, 그 결과 (같음 / 같지 않음 / 어느 쪽이 큰지 등)는 EFLAGS 상태 값을 통해 확인할 수 있다. 한편 jcc (Jump if condition is Met) 명령이 EFLAGS의 값을 참조하여 그것이 지정된 조건이라면 소정의 주소로 비행한다는 것이다. 즉 루프 제어 (예를 들어 루프를 1000 번 반복) 등에 cmp 또는 test 및 jcc는 세트로 사용되는 것이다.

 

[그림 4] Macro Fusion의 예. "CMP"며 "JCC '가

Decode 단계에서"cmp + jcc'라는 하나의 명령으로 정리되고 있다


 이러한 매우 복잡하게 나오는 것에 대해서는 위의 그림 4와 같이 Decode 단계에서 한 덩어리로 해 버려, 그대로 처리를 하여 μOp를 절약한다. 이것도 결국은 전력 절감과 성능 향상에 도움이 된다. MicroOps Fusion까지, 어디 까지나 " 하나의 x86 명령이 여러 μOp로 분해된다" 라는 것이지만 Macro Fusion는 결국 " 여러 x86 명령어가 하나의 μOp로 집약된다 " 가 된 셈이다. (참고로 Macro Fusion은 콘로(메롬,켄츠필드,펜린,울프데일,요크필드 등의 코어 마이크로 아키텍처) 아키텍처의 64bit 모드에서는 작동하지 않습니다 (Micro Ops Fusion 은 됩니다). 64bit에서도 32비트와 전혀 차이 없이 지원하는 것은 네할렘(1세대 " 코어 i " 시리즈) 부터 입니다.)

 여기까지 설명으로 눈치챈 분도 계실테지만, 최근 μOp는 이미 RISC 형 이라는 최초의 정의에서 꽤 먼 곳에 있다. RISC 대 CISC의 우열 논쟁도 90년대 들어 상당히 시들 해졌다. 정확히는 의미가 없어졌다.

 이유는 두 가지다. 하나는 CISC가 내부적으로 RISC 형의 처리를 하게 되고, 적어도 명령어 세트와 성능 사이에 직접적인 관계가 없어져 버린 것. 또 하나는 RISC 쪽도 이래저래 명령을 확장해 나가 결과적으로 CISC와 별로 차이가 없어져 버린 것이다.

 이에 따른 것은 아니겠지만, μOp도 대단히 확장을 이룬 결과 매우 복잡한 것으로 진화 해 버렸다. 다행인 것은 80년대와 달리, 어쨌든 사용할 트랜지스터 수가 방대하게 되었기 때문에, 다소 복잡한 명령어나 어려운 것도, 기법으로 해결할 수 있게 된 것이다.

 그래도 x86 명령어로 처리하지 않는 것은 x86 명령이 슈퍼 스칼라와 아웃 오브 오더를 고려하지 않은 명령 체계이기 때문이다. x86 명령을 효과적으로 처리하기 위해 한 번 변환하는 것이 좋은 셈이다.

 

 

[고전 2001.08.29] 베니어스 2003년 상반기 출시 발표, 3.5Ghz 펜티엄4 데모

 

 

[고전 2002.09.11] 이것이 Banias 플랫폼이다 CPU 마이크로 아키텍처 편

 

 

[분석정보] Intel의 차세대 CPU 아키텍처 Core Microarchitecture

 

 

[분석정보] 명확해진 Core Microarchitecture

 

 

[아키텍처] Core Microarchitecture 속도의 비밀은 CISC의 아름다움

 

 

2010년 10월 04일 기사입니다.

 

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

 

 

[분석정보] 명확해진 Core Microarchitecture


 

[아키텍처] Core Microarchitecture 속도의 비밀은 CISC의 아름다움

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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