맨위로가기

레지스터 이름 변경

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

레지스터 이름 변경은 CPU의 성능을 향상시키는 기술로, 명령어의 실행 순서를 변경하여 병렬 처리를 가능하게 한다. IBM System/360 Model 91에서 처음 사용되었으며, 1990년 POWER1은 레지스터 이름 변경을 사용한 최초의 마이크로프로세서이다. 이 기술은 비순차적 실행과 함께 사용되며, 하드웨어에서 레지스터 참조를 다른 레지스터로 변경하여 데이터 종속성 문제를 해결한다. 레지스터 이름 변경에는 태그 인덱싱된 레지스터 파일 방식과 예약 스테이션 방식이 있으며, 비순차적 명령어 처리, 재정렬 버퍼, 분기 예측과 같은 기술과 관련이 있다.

더 읽어볼만한 페이지

  • 컴퓨터 구조 - PA-RISC
    PA-RISC는 휴렛 팩커드에서 개발한 RISC 기반 명령어 집합 아키텍처로, HP 서버 및 워크스테이션에 사용되었으며 대용량 L1 캐시와 SIMD 명령어 확장 등의 특징을 가졌으나 아이테니엄 아키텍처로의 전환으로 단종되었다.
  • 컴퓨터 구조 - 메모리 관리
    메모리 관리는 운영체제의 핵심 기능으로, 여러 프로세스의 원활한 실행을 위해 메모리 공간을 할당하고 관리하며, 릴로케이션, 보호, 공유, 가상 메모리 관리, 자동/수동 메모리 관리 등의 기능을 수행한다.
레지스터 이름 변경
개요
유형컴퓨터 아키텍처 기술
목적프로세서 성능 향상
핵심 아이디어논리 레지스터를 물리 레지스터로부터 추상화
상세 내용
설명레지스터 이름 변경은 컴퓨터 아키텍처에서 사용되는 기술로, 프로세서가 레지스터 의존성으로 인한 정지를 피할 수 있도록 한다.
작동 원리프로그램에서 사용하는 논리 레지스터를 프로세서 내부의 물리 레지스터 세트에 동적으로 매핑하여 작동한다.
효과여러 명령어가 동시에 실행될 수 있도록 함.
프로세서의 전반적인 성능을 향상시킴.
역사
개발 배경레지스터 이름 변경은 원래 Tomasulo 알고리즘의 일부로 개발되었다.
최초 구현IBM System/360 Model 91에서 처음 구현되었다.
활용 분야
적용 대상슈퍼스칼라 프로세서
아웃 오브 오더 실행 프로세서
고성능 프로세서
예시인텔 펜티엄 프로, AMD 애슬론
장점 및 단점
장점레지스터 의존성으로 인한 병목 현상 감소
명령어 수준 병렬성 (ILP) 향상
파이프라인 정지 감소
단점추가적인 하드웨어 복잡성
더 많은 물리 레지스터 필요
전력 소비 증가 가능성
관련 기술
관련 기술Tomasulo 알고리즘
스코어보딩
아웃 오브 오더 실행
슈퍼스칼라

2. 역사적 배경

명령어 수준 병렬성(ILP)을 높여 프로세서 성능을 향상시키는 핵심 기술 중 하나인 레지스터 이름 변경은 명령어 간의 잘못된 데이터 종속성 문제를 해결하여 명령어의 비순차적 실행을 효율적으로 지원하기 위해 등장했다.

이 기법은 초기의 고성능 컴퓨터 시스템에서 처음 시도되었는데, 대표적으로 IBM System/360 Model 91은 토마술로 알고리즘의 일부로 레지스터 이름 변경을 구현하여 명령어 처리 성능을 높였다. 마이크로프로세서 시대에 들어서는 1990년 POWER1과 같은 RISC 프로세서에서 처음으로 레지스터 이름 변경과 비순차적 실행을 함께 구현했으며[4], 이후 x86 아키텍처에서도 1990년대 중반 Cyrix 6x86, AMD K5[2][3], 인텔의 P6 마이크로아키텍처 기반 프로세서 등에 도입되면서 널리 확산되었다.

기술적인 구현 방식 또한 초기에는 연관 메모리(CAM)를 활용하는 방식이 사용되었으나, 점차 현대적인 프로세서에서는 맵 테이블(map table)을 RAM으로 구현하고 논리 레지스터 번호를 인덱스로 사용하는 방식으로 발전해 왔다. 이러한 레지스터 이름 변경 기술의 발전은 프로세서가 더 많은 명령어를 동시에 처리할 수 있게 하여 컴퓨팅 성능 향상에 크게 기여했다.

2. 1. 초기 역사

IBM System/360 Model 91은 명령의 비순차적 실행을 지원하는 초기 기계 중 하나로, 레지스터 리네이밍을 사용하는 토마술로 알고리즘을 구현했다.

1990년에 등장한 POWER1은 레지스터 리네이밍과 비순차적 실행을 사용한 최초의 마이크로프로세서이다. POWER1은 부동소수점 로드 명령어에 대해서만 레지스터 리네이밍을 적용했는데, 이는 FPU가 하나뿐이었기 때문이다. 이후 등장한 POWER2는 여러 개의 FPU를 탑재하여 모든 부동소수점 명령어에 이름 변경 기법을 적용했다.[4]

2. 2. 발전과 확산

IBM System/360 Model 91은 명령어의 비순차적 실행을 지원한 초기 컴퓨터 중 하나로, 레지스터 이름 변경 기법을 사용하는 토마술로 알고리즘을 채택했다. 1990년에 등장한 POWER1은 레지스터 이름 변경과 비순차적 실행을 구현한 최초의 마이크로프로세서이다. 다만, POWER1은 부동소수점 로드 명령어에 대해서만 레지스터 이름 변경을 사용했는데, 이는 FPU가 하나뿐이어서 메모리 연산 외의 부동소수점 명령어에는 이름 변경이 불필요했기 때문이다. 이후 여러 개의 FPU를 탑재한 POWER2에서는 모든 부동소수점 명령어에 레지스터 이름 변경 기법이 적용되었다.[4]

R10000 프로세서의 초기 설계는 발행 큐(issue queue)의 크기가 작고 우선순위 인코딩 방식이 고정적이어서, 특정 명령어가 이름 변경에 필요한 레지스터를 할당받지 못하여 실행되지 못하고 전체 명령어 처리가 중단되는 '스타베이션(starvation)' 문제를 겪었다. 이후 R12000을 포함한 후속 설계에서는 이 문제를 완화하고자 부분적으로 가변 우선순위 인코딩 방식을 도입했다.

초기의 비순차적 실행 프로세서들은 이름 변경 기능과 명령어 재정렬 버퍼(ROB) 또는 물리 레지스터 파일(PRF) 같은 저장 기능을 명확하게 분리하지 않았다. 일부 초기 설계(예: Sohi의 RUU, Metaflow DCAF)에서는 스케줄링, 이름 변경, 데이터 저장을 하나의 통합된 구조에서 처리하기도 했다. 또한, 초기 프로세서들은 이름 변경을 위해 연관 메모리(CAM)를 사용했다. 예를 들어, HPSM의 RAT(Register Alias Table)는 논리 레지스터 번호와 그 버전 정보를 CAM을 이용하여 관리했다. 하지만 큰 규모의 CAM은 구현이 비효율적이므로, 비순차적 마이크로아키텍처는 점차 CAM 사용을 줄이는 방향으로 발전해 왔다. 현대의 프로세서들(예: P6, 퓨처 파일)은 대부분 맵 테이블(map table)을 RAM으로 구현하고 논리 레지스터 번호를 인덱스로 사용하여 이름 변경을 수행한다.

x86 아키텍처에서는 1995년 10월에 출시된 Cyrix 6x86이 레지스터 이름 변경과 비순차적 실행을 처음으로 도입한 프로세서였다. 이후 1996년에 출시된 NexGen Nx686과 AMD K5 역시 RISC μ-연산(마이크로 명령어) 수준에서 레지스터 이름 변경과 비순차적 실행 기능을 구현했다.[2][3] 인텔의 경우 P6 마이크로아키텍처를 통해 처음으로 비순차적 실행과 레지스터 이름 변경을 도입했으며, 이 아키텍처는 펜티엄 프로, 펜티엄 II, 펜티엄 III, 펜티엄 M, 코어, 코어 2 마이크로프로세서 등에 사용되었다.

3. 레지스터 리네이밍의 필요성

프로그램 실행 속도를 높이기 위해 현대 프로세서명령어를 순서대로 처리하지 않고 가능한 병렬로 처리하는 아웃 오브 오더 실행(비순차적 실행) 방식을 사용한다. 또한, 빠른 데이터 접근을 위해 메인 메모리보다 훨씬 빠른 프로세서 레지스터를 활용한다.

하지만 명령어 집합 아키텍처에서 사용할 수 있도록 정의된 레지스터(아키텍처 레지스터)의 수는 제한적이다. 이 때문에 프로그램 내에서 동일한 레지스터를 반복해서 사용(재사용)하는 경우가 필연적으로 발생한다.

이렇게 제한된 레지스터를 재사용하면서 명령어를 비순차적으로 실행하려고 할 때 문제가 생긴다. 서로 다른 연산에 속한 명령어들이 단지 같은 이름의 레지스터를 사용한다는 이유만으로 실행 순서에 제약을 받게 되는 가짜 종속성(False Dependency)이 발생하기 때문이다. 예를 들어, 아직 이전 명령어가 읽어야 할 값을 가진 레지스터에 다음 명령어가 새로운 값을 덮어쓰려 하거나(쓰기 후 읽기, WAR), 두 명령어가 같은 레지스터에 쓰려고 경쟁하는(쓰기 후 쓰기, WAW) 경우가 이에 해당한다.

이러한 가짜 종속성은 실제 데이터 처리 흐름과는 무관하게 명령어의 병렬 실행을 방해하여 아웃 오브 오더 실행의 성능 향상 효과를 크게 저해한다. 레지스터 리네이밍(Register Renaming)은 바로 이 문제를 해결하기 위해 필요하다. 하드웨어적으로 명령어에 명시된 레지스터 이름을 내부적으로 더 많은 물리적 레지스터 중 하나로 동적으로 바꿔 할당함으로써, 이름으로 인한 충돌(가짜 종속성)을 제거하고 명령어 수준 병렬성(ILP)을 극대화하여 프로세서 성능을 높이는 핵심 기술이다.

3. 1. 명령어 집합의 제약

프로그램은 값에 대해 작동하는 명령어들로 구성된다. 명령어는 처리할 값을 구별하기 위해 해당 값이 저장된 위치의 이름을 지정해야 한다. 예를 들어 "x와 y를 더해 결과를 z에 저장하라"는 명령어에서 x, y, z는 저장 위치의 이름이다.

프로세서는 자주 사용되는 값을 빠르게 접근하기 위해 프로세서 레지스터라는 고속 메모리 공간을 사용한다. 레지스터를 사용하면 메인 메모리에 접근하는 것보다 훨씬 빠르게 명령을 처리할 수 있으며, 이는 특히 RISC 설계의 핵심적인 성능 향상 요소이다. 프로세서 설계에서 사용 가능한 레지스터의 모음을 '레지스터 파일'이라고 부른다.

명령어 집합 아키텍처(ISA)는 프로그램이 사용할 수 있는 레지스터의 수와 종류를 제한적으로 정의한다. 이를 '아키텍처 레지스터'라고 부른다. 예를 들어, 알파 ISA는 32개의 정수 레지스터와 32개의 부동 소수점 레지스터를 정의한다. 다양한 아키텍처별 레지스터 수는 다음과 같다.

아키텍처범용/정수 레지스터 수
Zilog Z808개
IA-32 (x86)8개
x86-64 (AMD64)16개
PowerPC, 다수 RISC32개
IA-64128개



레지스터 수가 많으면 더 많은 임시 값을 저장하여 메모리 접근 횟수를 줄일 수 있어 성능 향상에 도움이 된다. 하지만 레지스터 수가 많아질수록 머신 코드 명령어 내에서 각 레지스터를 구분하기 위해 더 많은 비트가 필요하다. 예를 들어 32개의 레지스터를 구분하려면 5비트(2의 5제곱 = 32)가 필요하고, 64개의 레지스터를 구분하려면 6비트(2의 6제곱 = 64)가 필요하다. 명령어의 길이는 제한되어 있으므로, 레지스터 지정에 더 많은 비트를 사용하면 연산 종류나 다른 정보를 인코딩할 공간이 줄어든다. 따라서 프로세서 설계에서는 레지스터 파일 크기와 명령어 인코딩 효율성 사이의 균형을 맞추어야 한다.

이렇게 아키텍처 레지스터 수가 제한되어 있기 때문에 컴파일러가 코드를 최적화하는 데 어려움을 겪는다. 컴파일러는 제한된 수의 레지스터를 효율적으로 재사용해야 하는데, 이 과정에서 문제가 발생할 수 있다. 예를 들어, 어떤 레지스터가 특정 연산에 사용된 후, 그 결과가 필요한 다른 연산이 완료되기 전에 같은 레지스터에 다른 값을 저장하는 명령이 실행되면 프로그램 오류가 발생할 수 있다 (이를 '의존성 문제'라고 한다). 컴파일러는 가능한 경우 서로 다른 레지스터를 할당하여(일종의 레지스터 이름 변경) 이러한 문제를 피하고 명령어 실행 순서를 최적화하려 하지만, 사용 가능한 아키텍처 레지스터 수가 적으면 이러한 최적화는 제한적일 수밖에 없다. 즉, 컴파일러 수준에서 레지스터 재사용을 피하기 위해 코드를 재구성하면 코드 크기가 불필요하게 커지거나 성능 향상에 한계가 있을 수 있다.

3. 2. 비순차적 명령어 처리 (Out-of-Order Execution)

각기 다른 명령어는 처리하는 데 걸리는 시간이 다를 수 있다. 예를 들어, 프로세서는 메인 메모리에서 데이터를 로드하는 동안 수백 개의 레지스터 간 연산을 실행할 수 있다. 성능 향상의 핵심적인 발전 중 하나는, 다른 명령어가 데이터를 기다리는 동안 이러한 빠른 명령어들을 실행할 수 있도록 하는 것이다. 이는 명령어가 기계 코드에 지정된 순서대로 완료되지 않고, 대신 비순차적 실행(Out-of-Order Execution) 방식으로 처리될 수 있음을 의미한다. 최근의 고성능 CPU에서는 성능 향상을 위해 이러한 비순차적 실행 방식을 자주 사용한다.

비순차적 CPU에서 실행되는 다음 코드 조각을 살펴보자.

레지스터 리네이밍 적용 전 예시
#명령연산
1로드메모리 1024번지에서 레지스터 1(r1)로
2덧셈레지스터 1(r1)에 숫자 2를 더함
3저장레지스터 1(r1)의 내용을 1032번지로 저장
4로드메모리 2048번지에서 레지스터 1(r1)로
5덧셈레지스터 1(r1)에 숫자 4를 더함
6저장레지스터 1(r1)의 내용을 2056번지로 저장



명령 #4, #5, #6은 명령 #1, #2, #3과 서로 독립적이다. 즉, 수행하려는 작업 자체에는 의존 관계가 없다. 그러나 위 코드에서는 명령 #4가 명령 #3이 완료될 때까지 실행될 수 없다. 만약 명령 #3이 완료되기 전에 명령 #4가 실행되어 레지스터 1(r1)의 값을 바꿔버리면, 명령 #3은 잘못된 값을 메모리 1032번지에 저장하게 될 위험이 있다 (이는 쓰기 후 읽기 (WAR) 종속성 문제이다). 또한, 명령 #5는 명령 #4가 레지스터 1(r1)에 새로운 값을 로드한 후에 실행되어야 올바른 연산을 수행할 수 있다 (이는 읽기 후 쓰기 (RAW) 종속성 문제이다). 이는 두 명령어 그룹이 동일한 레지스터 'r1'을 사용하기 때문에 발생하는 문제이다.

이러한 제약은 레지스터 할당 과정에서 다른 레지스터를 사용함으로써 제거할 수 있다. 만약 컴파일러가 코드를 생성할 때, 또는 하드웨어가 동적으로 레지스터 이름을 변경(리네이밍)하여 아래와 같이 코드를 수정한다면, 두 명령어 그룹은 병렬로 실행될 수 있다.

레지스터 리네이밍 적용 후 예시
#명령연산
1로드메모리 1024번지에서 레지스터 1(r1)로
2덧셈레지스터 1(r1)에 숫자 2를 더함
3저장레지스터 1(r1)의 내용을 1032번지로 저장
4로드메모리 2048번지에서 레지스터 2(r2)
5덧셈레지스터 2(r2)에 숫자 4를 더함
6저장레지스터 2(r2)의 내용을 2056번지로 저장



이제 명령 #4, #5, #6은 명령 #1, #2, #3과 독립적으로 병렬 실행될 수 있다. 프로그램은 불필요하게 동일한 레지스터를 사용하여 발생했던 데이터 종속성 문제를 해결함으로써 더 빠르게 실행될 수 있다.

많은 고성능 CPU는 명령어 집합에 정의된 것보다 더 많은 물리적 레지스터를 가지고 있으며, 하드웨어 수준에서 명령어에 명시된 논리 레지스터(예: r1)를 내부의 물리 레지스터(예: rA, rB)로 동적으로 매핑하는 레지스터 리네이밍을 수행한다. 이를 통해 위 예시처럼 r1만 사용하는 원래 코드도 내부적으로는 다음과 같이 처리하여 병렬성을 높인다.



rA ≔ m[1024] ; r1 -> rA 로 이름 변경

rA ≔ rA + 2

m[1032] ≔ rA

rB ≔ m[2048] ; r1 -> rB 로 이름 변경

rB ≔ rB + 4

m[2056] ≔ rB



=== 데이터 종속성 문제 ===

데이터 종속성

둘 이상의 명령어가 특정 레지스터나 메모리 위치를 피연산자로 참조할 때, 이 명령어들을 원래 프로그램 순서와 다르게 실행하면 세 가지 종류의 데이터 종속성 문제(해저드)가 발생할 수 있다.

; 읽기 후 쓰기 (Read After Write, RAW): 레지스터나 메모리 위치에서 값을 읽을 때는 프로그램 순서상 가장 마지막에 해당 위치에 기록된 값을 읽어야 한다. 즉, 이전 쓰기 명령의 결과가 완전히 반영된 후에 읽기 명령이 수행되어야 한다. 이는 진정한 종속성(True Dependency) 또는 흐름 종속성(Flow Dependency)이라고 하며, 명령어 실행 순서를 반드시 지켜야 하므로 레지스터 리네이밍으로 해결할 수 없다.

; 쓰기 후 쓰기 (Write After Write, WAW): 특정 레지스터나 메모리 위치에 여러 번의 쓰기 명령이 연속될 경우, 프로그램 순서상 가장 마지막의 쓰기 결과가 최종적으로 남아야 한다. 이전의 쓰기 명령 결과는 무시될 수 있다. 이는 출력 종속성(Output Dependency)이라고 하며, 이름 때문에 발생하는 가짜 종속성(False Dependency)이다. 레지스터 리네이밍을 통해 각 쓰기 명령이 서로 다른 물리 레지스터를 사용하게 함으로써 동시에 실행될 수 있도록 해결할 수 있다. 필요하다면 순서상 앞선 쓰기 명령을 무효화(취소)하여 해결할 수도 있다.

; 쓰기 후 읽기 (Write After Read, WAR): 레지스터나 메모리 위치에서 값을 읽은 후, 프로그램 순서상 나중에 오는 명령이 해당 위치에 새로운 값을 쓰는 경우이다. 읽기 명령은 나중의 쓰기 명령이 실행되기 전에 완료되어, 쓰기 전의 원래 값을 읽어야 한다. 이는 반대 종속성(Anti-dependency)이라고 하며, 이것 역시 이름 때문에 발생하는 가짜 종속성(False Dependency)이다. 레지스터 리네이밍을 통해 쓰기 명령이 새로운 물리 레지스터를 사용하도록 하여 읽기 명령과의 충돌을 피함으로써 해결할 수 있다.

레지스터 리네이밍은 WAW과 WAR 같은 가짜 종속성을 제거하는 핵심 기술이다. 모든 읽기가 완료될 때까지 쓰기를 지연시키는 대신, 이전 값과 새 값의 두 복사본을 유지할 수 있다. 새 값을 쓰기 전에 프로그램 순서상 앞서는 읽기에는 이전 값을 제공하고, 쓰기 이후의 읽기에는 새 값을 제공한다. 가짜 종속성이 제거되면 비순차적 실행의 기회가 늘어난다. 이전 값을 필요로 하는 모든 읽기가 완료되면 이전 값 복사본은 폐기될 수 있다. 이것이 레지스터 리네이밍의 본질적인 개념이다.

읽고 쓸 수 있는 모든 것은 이름 변경이 가능하다. 주로 범용 레지스터나 부동 소수점 레지스터가 대상이지만, 플래그나 상태 레지스터, 심지어 개별 상태 비트도 이름 변경 대상이 될 수 있다. 메모리 위치도 이름 변경이 가능하지만, 레지스터 리네이밍만큼 일반적으로 수행되지는 않는다. (트랜스메타의 크루소 프로세서에 구현된 스토어 버퍼는 메모리 이름 변경의 한 예이다.)

프로그램이 레지스터를 즉시 재사용하지 않도록 컴파일러가 코드를 최적화하면 레지스터 리네이밍의 필요성이 줄어든다. 일부 명령어 집합(예: IA-64)은 이러한 이유로 매우 많은 수의 레지스터를 제공한다. 그러나 이 접근 방식에는 다음과 같은 한계가 있다.


  • 컴파일러가 코드 크기를 크게 늘리지 않고 레지스터 재사용을 피하기는 어렵다. 특히 루프 내에서 각 반복마다 다른 레지스터를 사용하려면 코드를 복제(루프 언롤링)하거나 자기 수정 코드를 사용해야 할 수 있다. (단, IA-64는 레지스터 로테이션이라는 기술로 이를 일부 회피한다.)
  • 레지스터 수가 많아지면 명령 내에서 레지스터를 지정하는 데 더 많은 비트가 필요해져 코드 크기가 커진다.
  • 많은 기존 명령어 집합은 역사적으로 적은 수의 레지스터를 정의했으며, 하위 호환성 문제로 이를 변경하기 어렵다.


코드 크기 증가는 명령어 캐시 미스 확률을 높여 프로세서가 다음 명령어를 기다리며 멈추는(스톨) 현상을 유발할 수 있으므로 중요한 문제이다.

4. 레지스터 리네이밍의 종류

컴퓨터 프로그램은 명령어 집합 아키텍처(ISA)에 정의된 제한된 수의 레지스터를 사용하여 데이터를 읽고 쓴다. 예를 들어 알파 ISA는 32개의 64비트 정수 레지스터와 32개의 64비트 부동소수점 레지스터를 정의하는데, 이를 아키텍처 레지스터라고 부른다. 프로그래머나 디버거는 이 아키텍처 레지스터를 통해 프로그램의 상태를 확인한다.

하지만 실제 CPU 내부에는 아키텍처 레지스터보다 훨씬 많은 수의 물리 레지스터가 존재할 수 있다. 예를 들어 알파 ISA를 구현한 알파 21264 프로세서는 80개의 정수 물리 레지스터와 72개의 부동소수점 물리 레지스터를 가졌다. 물리 레지스터를 더 많이 두는 주된 이유는 아웃 오브 오더 실행 과정에서 발생하는 데이터 의존성 문제, 특히 쓰기 후 읽기(Write-After-Read, WAR)나 쓰기 후 쓰기(Write-After-Write, WAW)와 같은 가짜 종속성(False Dependencieseng)을 해결하여 명령어 수준 병렬성을 높이기 위해서다. 이를 위해 아키텍처 레지스터 이름을 내부적으로 사용하는 물리 레지스터 이름(또는 태그)으로 바꾸는 기술이 바로 레지스터 이름 변경(Register Renamingeng)이다.

레지스터 이름 변경은 크게 두 가지 방식으로 나눌 수 있으며, 이는 주로 실행 유닛에 데이터를 저장하는 레지스터 파일의 구조적 차이에 따라 구분된다. 두 방식 모두 명령어에서 사용된 아키텍처 레지스터를 내부적인 태그(Tageng)로 변환하여 물리 레지스터를 식별하는 과정을 포함한다. 이 태그는 일반적으로 아키텍처 레지스터 번호보다 더 많은 비트를 사용하여 더 많은 수의 물리 레지스터를 구별할 수 있게 해준다.

레지스터 이름 변경의 주요 두 가지 스타일은 다음과 같다.


  • 태그 인덱싱된 레지스터 파일: 모든 태그에 대응하는 물리 레지스터를 하나의 큰 중앙 레지스터 파일(물리 레지스터 파일, PRF)에 두고, 태그를 직접 인덱스로 사용하여 데이터에 접근하는 방식이다.
  • 예약 스테이션: 각 실행 유닛마다 비교적 작은 크기의 레지스터 파일(예약 스테이션)을 여러 개 두어 데이터를 분산하여 관리하는 방식이다.


이러한 레지스터 이름 변경 기법들은 주로 범용 레지스터나 부동소수점 레지스터에 적용되지만, 상태 레지스터나 플래그 비트 등 다른 종류의 저장 공간에도 적용될 수 있다. 드물게는 메모리 영역에 대해서도 유사한 기법이 사용되기도 한다. 예를 들어 트랜스메타의 Crusoe 프로세서가 사용한 스토어 버퍼는 일종의 메모리 리네이밍으로 볼 수 있다.

만약 명령어 집합 자체가 매우 많은 수의 레지스터를 제공하여 컴파일러 단계에서 레지스터 재사용을 최소화할 수 있다면(예: IA-64), 하드웨어적인 레지스터 이름 변경의 필요성은 줄어들 수 있다. 하지만 이는 레지스터 식별 비트 증가로 인한 명령어 워드 크기 증가나, 루프 처리의 복잡성 증가(코드 크기 증가) 등의 다른 문제를 야기할 수 있다.

4. 1. 태그 인덱싱된 레지스터 파일 (Tag-Indexed Register File)

MIPS R10000, 알파 21264, AMD 애슬론의 FP(부동소수점) 섹션 등에서 사용된 레지스터 이름 변경 방식이다. 이 방식은 데이터 저장을 위해 태그로 인덱싱되는 하나의 큰 '''물리 레지스터 파일'''(Physical Register File, PRF)을 사용하는 것이 특징이다. 즉, 모든 태그에 대해 하나의 레지스터가 할당된 큰 레지스터 파일을 중앙에 두는 구조이다. 예를 들어, 80개의 물리적 레지스터가 있다면 7비트 태그를 사용할 수 있으며, 이 경우 일부 태그 값은 사용되지 않을 수 있다.

동작 과정은 다음과 같다.

1. 이름 변경(Renaming): 명령어에서 참조하는 아키텍처 레지스터(읽기 또는 쓰기)는 아키텍처 레지스터 번호로 인덱싱된 '''리맵 파일'''(Remap File)에서 조회된다. 리맵 파일은 해당 아키텍처 레지스터에 매핑된 물리 레지스터의 '''태그'''(Tag)와 준비 상태를 나타내는 '''레디 비트'''(Ready Bit)를 반환한다.

  • 만약 해당 레지스터에 값을 쓸 예정인 이전 명령어가 아직 완료되지 않았다면, 레디 비트는 '준비되지 않음(not ready)' 상태를 나타낸다.
  • 읽기 피연산자의 경우, 명령어의 아키텍처 레지스터 지정자는 리맵 파일에서 얻은 태그로 교체된다.
  • 쓰기 피연산자의 경우, '''자유 태그 FIFO'''(Free Tag FIFO)에서 새로운 태그를 할당받는다. 이 새로운 태그와 아키텍처 레지스터 간의 매핑 정보가 리맵 파일에 기록되며, 상태는 '준비되지 않음'으로 표시된다. 이전에 해당 아키텍처 레지스터에 할당되었던 물리 레지스터(이전 태그)는 명령어와 함께 '''재정렬 버퍼'''(Reorder Buffer, ROB)에 저장된다. 재정렬 버퍼는 명령어를 프로그램의 원래 순서대로 유지하는 FIFO 구조이다.

2. 발행 큐 배치: 이름 변경이 완료된 명령어는 연산 종류 등에 따라 적절한 '''발행 큐'''(Issue Queue)에 배치된다.

3. 준비 상태 확인: 명령어가 실행 유닛에서 실행되어 결과값을 내면, 해당 결과값과 연관된 태그가 시스템 전체에 브로드캐스트된다. 발행 큐는 이 태그를 기다리고 있는(준비되지 않은) 소스 피연산자의 태그와 비교한다. 일치하는 태그를 가진 피연산자는 '준비됨(ready)' 상태로 변경된다. 리맵 파일 역시 브로드캐스트된 태그를 확인하여 해당 물리 레지스터를 '준비됨' 상태로 표시한다.

4. 명령어 발행(Issue): 발행 큐에 있는 명령어의 모든 피연산자가 '준비됨' 상태가 되면, 해당 명령어는 실행 유닛으로 발행될 준비가 된다. 발행 큐는 매 사이클마다 준비된 명령어를 선택하여 적절한 기능 유닛(Functional Unit)으로 보낸다. 아직 준비되지 않은 명령어는 발행 큐에 남아 대기한다. 발행 큐에서 준비된 명령어를 순서에 상관없이 내보내는(out-of-order) 방식은 성능 향상에 기여한다.

5. 실행(Execute): 발행된 명령어는 태그를 이용해 물리 레지스터 파일에서 필요한 피연산자 값을 읽어온다. 이때, 방금 계산되어 브로드캐스트된 값을 레지스터 파일을 거치지 않고 바로 사용하는 바이패스(Bypass) 경로를 활용할 수도 있다. 읽어온 값으로 기능 유닛에서 연산을 수행한다.

6. 결과 기록 및 완료(Writeback & Commit): 실행 결과는 해당 명령어의 목적지 레지스터에 할당된 태그와 함께 물리 레지스터 파일에 기록된다. 동시에 결과값과 태그는 다시 브로드캐스트되어 후속 명령어들이 사용할 수 있게 한다. 명령어가 최종적으로 완료(Commit)되면, 이전에 해당 아키텍처 레지스터에 매핑되었던 오래된 태그는 자유 태그 FIFO로 반환되어 다른 명령어가 재사용할 수 있게 된다.

예외(Exception)나 분기 예측 실패(Branch Misprediction)가 발생하면, 리맵 파일의 상태를 오류가 발생하기 직전의 올바른 상태로 복구해야 한다. 이는 상태 스냅샷과 재정렬 버퍼에 저장된 이전 태그 정보를 이용하여 이루어진다. 이 복구 메커니즘 덕분에 분기 예측 실패 시 분기 명령어가 완료되기 전에도 파이프라인 상태를 복구할 수 있어, 예측 실패로 인한 성능 저하를 줄이는 데 도움이 된다.

이 방식은 예약 스테이션 방식에 비해 구조가 비교적 간단하고, 특히 예외나 분기 예측 실패 시 상태 복구가 용이하다는 장점이 있다. 하지만 모든 레지스터 값을 하나의 큰 중앙 집중식 파일에서 관리하므로, 파일의 크기가 커지고 접근 포트 수가 많아지면 레지스터 접근 지연 시간이 늘어날 수 있다는 잠재적인 단점이 지적되기도 한다. 예약 스테이션 방식은 이름 변경 단계에서 바로 값을 찾는 경향이 있어 지연 시간이 짧을 수 있지만, 태그 인덱싱 방식은 태그를 먼저 찾고 그 태그로 값을 찾아야 하므로 상대적으로 지연 시간이 길어질 수 있다.

4. 2. 예약 스테이션 (Reservation Stations)

'''예약 스테이션'''(Reservation Stationseng) 방식은 레지스터 이름 변경을 구현하는 한 가지 스타일로, AMD K7 및 AMD K8 설계의 정수 처리 부분에서 사용되었다. 이 방식은 각 실행 유닛마다 대응하는 작은 레지스터 파일들을 여러 개 두는 것이 특징이다. 이는 모든 태그에 대한 레지스터를 하나의 큰 레지스터 파일에 두는 '태그-인덱싱된 레지스터 파일' 방식과 대비된다.

== 동작 방식 ==

예약 스테이션 방식의 동작 과정은 다음과 같다.

1. 리네이밍 단계: 명령어에서 읽기 참조되는 아키텍처 레지스터는 아키텍처 레지스터 번호로 인덱싱된 '''미래 파일'''(Future Fileeng)과 이름 변경 파일(Rename Fileeng) 모두에서 조회된다. 만약 해당 레지스터에 대한 쓰기 명령어가 아직 처리 중이지 않다면(즉, 준비된 상태라면), 미래 파일에서 해당 레지스터의 값을 직접 읽어온다.

2. 발행 큐 배치: 명령어가 발행 큐(Issue Queueeng)에 들어가면, 미래 파일에서 읽은 값은 해당 명령어에 대응하는 예약 스테이션 항목에 기록된다. 명령어에 의한 레지스터 쓰기는 아직 준비되지 않은 새로운 태그를 생성하고, 이 태그는 이름 변경 파일에 기록된다. 태그 번호는 일반적으로 명령어 순서대로 할당된다.

3. 태그 대기 및 브로드캐스트: 발행 큐는 아직 준비되지 않은 피연산자(Operandeng)가 있다면, 해당 태그가 브로드캐스트되기를 기다린다. 태그-인덱싱 방식과 유사하게 일치하는 태그를 감시하지만, 예약 스테이션 방식에서는 태그가 일치하면 해당 브로드캐스트 값을 예약 스테이션 항목에도 기록한다는 차이점이 있다.

4. 실행: 발행된 명령어는 예약 스테이션에서 필요한 인수(Argumenteng) 값을 읽어 실행한다. 예약 스테이션의 레지스터 파일은 일반적으로 항목 수가 8개 정도로 작다.

5. 결과 기록: 실행 결과는 재정렬 버퍼(Reorder Buffereng, ROB)에 기록된다. 또한, 발행 큐에 해당 태그를 기다리는 다른 명령어가 있다면 예약 스테이션에도 기록되고, 만약 이 결과가 해당 아키텍처 레지스터의 가장 최신 값이라면 미래 파일에도 기록되어 해당 레지스터가 준비된 상태로 표시된다.

6. 완료(Retirementeng): 명령어 실행이 최종 완료되면, 결과 값은 재정렬 버퍼에서 아키텍처 레지스터 파일(Architecture Register Fileeng 또는 Retirement Register Fileeng, RRF)로 복사된다. 아키텍처 레지스터 파일은 주로 예외 처리나 분기 예측 실패 시 상태를 복구하는 데 사용된다.

7. 예외 및 분기 예측 실패 처리: 완료 단계에서 예외나 분기 예측 실패가 감지되면, 아키텍처 레지스터 파일의 내용을 미래 파일로 복사하고 이름 변경 파일의 모든 레지스터를 준비된 상태로 표시하여 이전 상태로 복구한다. 하지만 일반적으로 디코딩과 완료 사이의 특정 시점 상태를 정확히 재구성하기는 어려워, 분기 예측 실패를 조기에 복구하는 것은 제한적이다.

== 장점 ==

  • 지연 시간 감소:
  • 리네이밍 단계에서 실행까지의 지연 시간이 짧다. 이는 리네이밍 단계에서 물리 레지스터 번호를 찾고 다시 값을 찾는 대신, 미래 파일에서 직접 값을 조회하기 때문이다. 이 지연 시간 단축은 특히 분기 예측 실패 시 복구 시간을 줄이는 데 기여한다.
  • 명령어 발행에서 실행까지의 지연 시간도 짧다. 각 실행 유닛에 연결된 로컬 예약 스테이션의 레지스터 파일이 태그-인덱싱 방식의 큰 중앙 레지스터 파일보다 작기 때문이다.
  • 단순화된 처리: 태그 생성 및 예외 처리가 태그-인덱싱 방식보다 상대적으로 간단하다.


== 단점 ==

  • 복잡한 하드웨어: 예약 스테이션에서 사용되는 레지스터 파일 시스템은 사용되지 않는 항목을 채우는 기능과 발행 큐 처리를 병행해야 하므로, 태그-인덱싱 방식의 레지스터 파일보다 구조가 더 복잡하다.
  • 많은 바이패스 네트워크: 예약 스테이션의 각 항목은 모든 결과 버스(Result Buseng)로부터 쓰기가 가능해야 한다. 예를 들어, 기능 유닛당 8개의 발행 큐 항목을 가진 예약 스테이션 구조는 동등한 태그-인덱싱 구조보다 훨씬 많은 바이패스 네트워크(결과를 필요한 곳으로 전달하는 경로)를 요구한다. 이로 인해 결과 전달에 더 많은 전력과 칩 면적이 소모된다.
  • 분산된 결과 저장 위치: 결과 값이 미래 파일, 예약 스테이션, 재정렬 버퍼, 아키텍처 레지스터 파일 네 곳에 저장될 수 있다. 반면 태그-인덱싱 방식은 물리 레지스터 파일 한 곳에만 저장한다. 여러 위치로 결과를 브로드캐스트하고 관리해야 하므로 더 많은 전력, 면적, 시간이 소요된다.


결론적으로, 예약 스테이션 방식은 실행 지연 시간 단축에 강점이 있어 매우 정확한 분기 예측 메커니즘과 결합될 경우 효과적일 수 있지만, 하드웨어 복잡성과 자원 소모 측면에서는 불리한 점이 있다.

5. 관련 기술

레지스터 이름 변경 기술은 현대 CPU의 성능을 높이기 위해 다른 여러 기술과 함께 사용된다. 특히 명령어 처리 순서를 최적화하여 명령어 수준 병렬성(Instruction-Level Parallelism, ILP)을 높이는 비순차적 실행(Out-of-Order Execution)과 밀접한 관련이 있다. 비순차적 실행 과정에서 발생할 수 있는 거짓 종속성(false dependency) 문제를 해결하는 데 레지스터 이름 변경이 중요한 역할을 수행한다.

5. 1. 비순차적 명령어 처리 (Out-of-Order Execution)

명령어 종류에 따라 처리 시간이 다르다. 예를 들어, 프로세서는 메인 메모리에서 데이터를 읽어오는 동안 수백 개의 레지스터 간 연산을 수행할 수 있다. 성능 향상을 위해, 시간이 오래 걸리는 명령(예: 메모리 로드)을 기다리는 동안 다른 빠른 명령들을 먼저 처리하는 기법이 사용되는데, 이를 비순차적 실행(Out-of-Order Execution)이라고 한다. 이 방식에서는 명령어가 프로그램 코드에 명시된 순서와 다르게 실행될 수 있다.

다음은 비순차적 실행 CPU에서 실행되는 코드 예시이다.

레지스터 리네이밍 적용 전
#명령연산
1로드메모리 1024번지에서 레지스터 1로
2덧셈레지스터 1에 숫자 2를 더함
3저장레지스터 1의 내용을 1032번지로 저장
4로드2048번지에서 레지스터 1로
5덧셈레지스터 1에 숫자 4를 더함
6저장레지스터 1의 내용을 2056번지로 저장



여기서 4, 5, 6번 명령어는 1, 2, 3번 명령어와 내용상으로는 독립적이다. 하지만 4번 명령어는 3번 명령어가 완료될 때까지 기다려야 한다. 만약 4번 명령어가 먼저 실행되어 레지스터 1의 값을 바꿔버리면, 3번 명령어가 잘못된 값을 메모리에 저장할 수 있기 때문이다. 이는 실제 데이터 의존성이 아닌, 같은 레지스터 이름(`레지스터 1`)을 재사용하면서 발생하는 거짓 종속성(false dependency) 또는 이름 종속성(name dependency) 때문이다.

이러한 거짓 종속성은 레지스터 이름 변경을 통해 해결할 수 있다. 즉, 서로 다른 명령어 그룹에 다른 레지스터를 할당하는 것이다.

레지스터 리네이밍 적용 후
#명령연산
1로드메모리 1024번지에서 레지스터 1로
2덧셈레지스터 1에 숫자 2를 더함
3저장레지스터 1의 내용을 1032번지로 저장
4로드2048번지에서 레지스터 2
5덧셈레지스터 2에 숫자 4를 더함
6저장레지스터 2의 내용을 2056번지로 저장



이렇게 레지스터를 분리하면, 4, 5, 6번 명령어는 1, 2, 3번 명령어의 완료를 기다릴 필요 없이 병렬로 실행될 수 있다. 이를 통해 명령어 수준 병렬성(Instruction-Level Parallelism, ILP)을 높여 프로그램 실행 속도를 향상시킨다.

레지스터 리네이밍으로 병렬 실행 가능해짐
#명령(X)연산(X)명령(Y)연산(Y)
1로드메모리 1024번지에서 레지스터 1로NOP(메모리 로드는 일반적으로 병렬 실행 어려움)
2덧셈레지스터 1에 숫자 2를 더함로드2048번지에서 레지스터 2
3저장레지스터 1의 내용을 1032번지로 저장덧셈레지스터 2에 숫자 4를 더함
4NOP대기저장레지스터 2의 내용을 2056번지로 저장



컴파일러는 코드 생성 단계에서 레지스터 할당을 통해 이러한 이름 변경을 시도할 수 있다. 하지만 프로그래머가 직접 접근할 수 있는 아키텍처 레지스터(예: x86의 8개 정수 레지스터, AMD64의 16개 등)의 수는 제한적이므로 컴파일러만으로는 한계가 있다. 따라서 많은 고성능 CPU는 명령어 집합에 정의된 아키텍처 레지스터보다 더 많은 수의 물리적 레지스터를 내부에 가지고 있다. 그리고 하드웨어 수준에서 동적으로 명령어의 레지스터 이름을 내부의 물리적 레지스터로 재할당(이름 변경)하여 거짓 종속성을 제거하고, 비순차적 실행을 통해 명령어 수준 병렬성을 극대화한다.

참조

[1] 웹사이트 Cyrix 6x86 Processor https://web.archive.[...]
[2] 간행물 NexGen Details Sixth Generation Nx686 Processor Technology http://www.cpushack.[...]
[3] 논문 100 MHz and Beyond https://books.google[...] Ziff Davis 1994-12-06
[4] 논문 The design space of register renaming techniques http://www.eecs.umic[...] IEEE 2000-09



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com