32 비트에서는 8 개의 "범용"레지스터가있었습니다. 64 비트를 사용하면 양이 두 배가되지만 64 비트 변경 자체와는 독립적 인 것 같습니다.
이제 레지스터가 너무 빠르면 (메모리 액세스 없음) 왜 자연스럽게 더 많은 레지스터가 없을까요? CPU 빌더는 CPU에서 가능한 한 많은 레지스터를 작동해야하지 않습니까? 우리가 가진 양만 가지고있는 이유에 대한 논리적 제한은 무엇입니까?
32 비트에서는 8 개의 "범용"레지스터가있었습니다. 64 비트를 사용하면 양이 두 배가되지만 64 비트 변경 자체와는 독립적 인 것 같습니다.
이제 레지스터가 너무 빠르면 (메모리 액세스 없음) 왜 자연스럽게 더 많은 레지스터가 없을까요? CPU 빌더는 CPU에서 가능한 한 많은 레지스터를 작동해야하지 않습니까? 우리가 가진 양만 가지고있는 이유에 대한 논리적 제한은 무엇입니까?
답변:
레지스터 수가 많지 않은 데는 여러 가지 이유가 있습니다.
요즘 우리는 정말 많은 레지스터를 가지고 있습니다-그것들은 단지 명시 적으로 프로그래밍 된 것이 아닙니다. "등록 이름 변경"이 있습니다. 작은 세트 (8-32 레지스터)에만 액세스하지만 실제로는 훨씬 더 큰 세트 (예 : 64-256)에 의해 지원됩니다. 그런 다음 CPU는 각 레지스터의 가시성을 추적하고 이름이 변경된 세트에 할당합니다. 예를 들어, 레지스터에 여러 번로드, 수정 및 저장할 수 있으며 캐시 미스 등에 따라 이러한 각 작업을 실제로 독립적으로 수행 할 수 있습니다. ARM에서 :
ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]
Cortex A9 코어는 레지스터 이름 변경을 수행하므로 "r0"에 대한 첫 번째로드는 실제로 이름이 변경된 가상 레지스터로 이동합니다.이를 "v0"이라고하겠습니다. 로드, 증가 및 저장은 "v0"에서 발생합니다. 한편, r0에 대한로드 / 수정 / 저장도 다시 수행하지만, r0을 사용하는 완전히 독립적 인 시퀀스이기 때문에 "v1"로 이름이 변경됩니다. 캐시 미스로 인해 "r4"의 포인터에서로드가 중단되었다고 가정 해 보겠습니다. 괜찮습니다. "r0"이 준비 될 때까지 기다릴 필요가 없습니다. 이름이 바뀌었기 때문에 "v1"(또한 r0에 매핑 됨)을 사용하여 다음 시퀀스를 실행할 수 있습니다. 아마도 그것은 캐시 히트이고 우리는 방금 엄청난 성능 승리를 거두었습니다.
ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]
x86은 요즘 이름이 바뀐 레지스터 (야구장 256)가 엄청나게 많다고 생각합니다. 이는 소스와 대상이 무엇인지 말하기 위해 모든 명령어에 대해 8 비트 x 2를 갖는 것을 의미합니다. 코어 전체에 필요한 와이어 수와 크기가 크게 늘어납니다. 따라서 대부분의 디자이너가 결정한 16-32 개의 레지스터에 대한 스위트 스팟이 있으며, 비 순차적 인 CPU 디자인의 경우 레지스터 이름 변경이이를 완화하는 방법입니다.
편집 : 순서가 틀린 실행의 중요성과 이에 대한 이름 변경 등록. OOO가 있으면 레지스터의 수는 그다지 중요하지 않습니다. 왜냐하면 그들은 단지 "임시 태그"이고 훨씬 더 큰 가상 레지스터 세트로 이름이 변경되기 때문입니다. 작은 코드 시퀀스를 작성하기가 어려워 지므로 숫자가 너무 작 으면 안됩니다. 이것은 x86-32의 문제입니다. 왜냐하면 제한된 8 개의 레지스터는 많은 임시가 스택을 통과한다는 것을 의미하고 코어는 읽기 / 쓰기를 메모리로 전달하기위한 추가 로직이 필요하기 때문입니다. OOO가 없으면 일반적으로 작은 코어에 대해 이야기합니다.이 경우 큰 레지스터 세트는 낮은 비용 / 성능 이점입니다.
따라서 대부분의 CPU 클래스에 대해 약 32 개의 아키텍처 레지스터로 최대가되는 레지스터 뱅크 크기에 대한 자연스러운 스윗 스팟이 있습니다. x86-32에는 8 개의 레지스터가 있으며 확실히 너무 작습니다. ARM은 16 개의 레지스터를 사용했으며 이는 좋은 타협입니다. 32 개의 레지스터는 약간 너무 많습니다. 마지막 10 개 정도는 필요하지 않습니다.
이 중 어느 것도 SSE 및 기타 벡터 부동 소수점 보조 프로세서에 대해 얻는 추가 레지스터를 건드리지 않습니다. 그것들은 정수 코어와 독립적으로 실행되고 CPU의 복잡성을 기하 급수적으로 증가시키지 않기 때문에 추가 세트로 합리적입니다.
거의 모든 명령어가 구조적으로 볼 수있는 1, 2 또는 3 개의 레지스터를 선택해야하기 때문에 그 수를 늘리면 각 명령어에서 코드 크기가 몇 비트 씩 증가하여 코드 밀도가 감소합니다. 또한 스레드 상태로 저장해야하고 함수의 활성화 레코드 에 부분적으로 저장해야하는 컨텍스트 의 양도 증가합니다 . 이러한 작업은 자주 발생합니다. 파이프 라인 인터록은 모든 레지스터에 대해 점수 판을 확인해야하며 이는 2 차 시간 및 공간 복잡성이 있습니다. 그리고 아마도 가장 큰 이유는 이미 정의 된 명령어 세트와의 호환성 때문일 것입니다.
그러나하기 위해 감사를 밝혀 이름을 변경 등록 , 우리가 정말 가능한 레지스터를 많이해야합니까, 우리는 심지어 저장할 필요가 없습니다. CPU에는 실제로 많은 레지스터 세트가 있으며 코드가 실행될 때 자동으로 이들 사이를 전환합니다. 순전히 더 많은 레지스터를 얻기 위해이 작업을 수행합니다.
예:
load r1, a # x = a
store r1, x
load r1, b # y = b
store r1, y
r0-r7 만있는 아키텍처에서 다음 코드는 CPU에 의해 다음과 같이 자동으로 다시 작성 될 수 있습니다.
load r1, a
store r1, x
load r10, b
store r10, y
이 경우 r10은 r1을 일시적으로 대체하는 숨겨진 레지스터입니다. CPU는 r1의 값이 첫 번째 저장 후 다시 사용되지 않는다는 것을 알 수 있습니다. 이를 통해 두 번째로드 또는 두 번째 저장소의 지연없이 첫 번째로드가 지연 될 수 있습니다 (온칩 캐시 적중에도 일반적으로 몇주기가 소요됨).
여기에 약간의 흥미로운 정보를 추가하려면 8 개의 동일한 크기의 레지스터를 사용하면 opcode가 16 진수 표기법과 일관성을 유지할 수 있음을 알 수 있습니다. 예를 들어 명령어 push ax는 x86에서 opcode 0x50이고 마지막 레지스터 di에 대해 0x57까지 올라갑니다. 그런 다음 명령 pop ax은 0x58에서 시작하여 0x5F까지 올라가 pop di첫 번째 16 진법을 완성합니다. 16 진수 일관성은 크기 당 8 개의 레지스터로 유지됩니다.