*컴퓨터 구조 및 설계 5th Edition (저자 David A. Patterson 과 John L. Hennessy)를 기반으로 작성되었습니다.

*MIPS 어셈블리언어 기반으로 작성되었습니다.

 

하드웨어 설계의 3대 원칙

 

1. 간단하게 하기 위해서는 규칙적인 것이 좋다.

덧셈 같은 연산의 피연산자(operand)는 더해질 숫자 두 개와 합을 기억할 장소 하나, 총 3개인 것이 자연스럽다. 이렇듯 모든 명령어가 피연산자를 반드시 세 개씩 갖도록 제한하는 것은 하드웨어를 간단하게 할 수 있는 방법이다. 

반대로 피연산자(operand)에 대한 변수 갯수가 가변적이라면 하드웨어가 매우 복잡해질 것이다. 

 

이런 관점에서 하드웨어 설계의 3대 원칙 중 첫번째를 도출할 수 있다. 

 

2. 작은 것이 더 빠르다.

MIPS는 보통 32개의 레지스터를 사용한다. 레지스터가 아주 많아지면 전기신호가 더 멀리까지 전달되어야 하므로 클럭 사이클 시간이 길어진다. 

 

물론 32개가 아닌 31개로 구성하면 빨라진다는 것이 아니다. 

따라서, 컴퓨터 설계자는 더 많은 레지스터를 원하는 프로그램의 갈망과 클럭 사이클을 빠르게 하고 싶은 본인의 바람 사이에서 적절한 타협점을 찾아야 할 것이다.

 

*레지스터가 메모리보다 작으면서 속도는 더 빠른데 여기에도 해당이 되는 원칙이다.

 

3. 자주 생기는 일을 빠르게 하라.

프로그램의 연산에서 상수를 사용하는 경우는 많이 있다. SPEC CPU2006 벤치마크를 실행해보면 MIPS의 산술 명령의 절반 이상이 상수를 피연산자로 사용함을 알 수 있다. 상수를 메모리에서 읽는 방식이 아니라, 상수 필드를 갖는 산술 명령어를 통해 사용하면 연산이 훨씬 빨리지고 에너지를 덜 소모하게 된다.

 

*update(2022.09.29)

4. 좋은 설계에는 적당한 절충이 필요하다. 

예를 들어, MIPS 명령어 필드에서 5비트 필드 중 하나를 주소로 쓴다면 $2^5 = 32$ 보다 작은 값만을 사용할 수 있다. 이 필드는 큰 배열이나 자료구조에서 한 원소를 선택하는데 사용되므로 32보다 큰 값이 필요한 경우가 많아 5bit 필드로는 부족하다.  이러한 문제 때문에 모든 명령어의 길이를 같게 하고 싶은 생각과 명령어 형식을 한가지로 통일하고 싶은 생각 사이에서 충돌이 생긴다. 

이와 같은 상황에서, 컴퓨터 설계자는 적당한 절충안을 통해 설계를 진행하여야 한다.

 

피연산자

상위 수준 언어 프로그램과는 달리 산술 명령어의 피연산자에는 제약이 있습니다. 레지스터(register)라고 하는 하드웨어로 직접 구현된 특수 위치만 사용할 수 있다는 것입니다. 또한, 프로그래밍 언어에서 사용하는 변수와 하드웨어 레지스터의 큰 차이점 하나는 레지스터는 개수가 한정되어 있다는 점입니다. 보통 32개의 레지스터로 구성되어 있는데, 이는 산술 명령어의 각 피연산자는 32개의 32비트 레지스터 중 하나이어야 한다는 제약이 추가됩니다. 

 

*MIPS 구조에서 레지스터의 크기는 32bit 이며, 워드(word)라고 부른다. 

 

프로그래밍 언어에는 값 하나만 기억하는 단순 변수 외에 배열(array)나 구조체(structure) 같은 복잡한 자료구조가 있습니다.

이러한 구조는 메모리(memory)에 보관합니다.

다만, 위에서 설명한 바와 같이 MIPS의 산술연산은 레지스터에서만 실행되므로 메모리와 레지스터간의 데이터를 주고받은 명령어가 있어야 합니다. 이러한 명령어를 데이터 전송 명령어(data transfer instruction)이라고 합니다.

메모리에 기억된 데이터 워드는 주소(Address)를 통해 접근하게 됩니다.

 

*적재(load): 메모리에서 레지스터로 데이터를 복사해 오는 데이터 전송 명령. 적재 명령은 연산자 이름, 메모리에서 읽어 온 값을 저장할 레지스터, 메모리  접근에 사용할 상수와 레지스터로 구성된다.

*저장(store): 레지스터에서 메모리로 데이터를 보내는 데이터 전송 명령. 저장 명령은 연산자 이름, 저장할 데이터를 갖고 있는 레지스터, 배열 원소 선택에 사용할 변위, 베이스 레지스터로 구성된다. 

 

변수를 레지스터와 연관 짓는 일 뿐만 아니라 배열이나 구조체 같은 자료구조를 메모리에 할당하는 것은 컴파일러의 임무입니다. 이러한 작업이 진행되어야 컴파일러가 자료구조의 시작 주소를 데이터 전송 명령에 넣을 수 있게 됩니다.

주소는 프로그램에서 8bit로 구성된 바이트(Byte)를 많이 사용하므로, 대부분의 컴퓨터는 바이트 단위로 주소를 지정합니다. 

워드 주소는 워드를 구성하는 4바이트 중 하나를 사용하므로, 연속된 워드의 주소는 4씩 차이나게 됩니다. 

컴퓨터 별로 워드 주소를 사용하는 방법은 두가지가 있습니다. 제일 왼쪽, 즉 최상위(big end) 바이트 주소를 워드 주소로 사용하는 방식과 제일 오른쪽, 즉 최하위(little end) 바이트 주소를 워드 주소로 사용하는 방법이 있습니다.

 

컴퓨터가 갖고 있는 레지스터보다 프로그램에서 사용하는 변수가 더 많은 경우가 자주 있습니다. 레지스터의 개수는 제한되어 있으므로 컴파일러는 자주 사용되는 변수를 가능한 한 많이 레지스터에 담고 나머지 변수는 메모리에 저장했다가 필요할 때 꺼내서 레지스터에 넣습니다. 

*이때 활용되는 기법으로, 자주 사용하지 않는(또는 한참 후에 사용할) 변수를 메모리에 넣는 일을 레지스터 스필링(Register spilling)이라고 한다.

"작을 수록 빠르다 라는 원칙에 의하면 레지스터가 더 작으므로 메모리보다 속도가 느려야한다. 실제로 레지스터가 더 빠르며, 데이터가 레지스터에 있으면 더 빠르게 접근할 수 있다. 

레지스터는 메보리보다 접근시간이 짧고 처리량도 많으므로, 레지스터에 저장된 데이터를 사용하면 시간이 절약되고 사용하기도 간편하다. 뿐만 아니라, 에너지도 절약된다.

따라서, 좋은 성능을 얻고 에너지를 절약하기 위해서는 컴파일러가 레지스터를 효율적으로 사용하여야한다.

 

 

+ Recent posts