04장에서 학습한 내용
1) 컴퓨터 부품들은 ‘클럭 신호’에 맞춰 일사분란하게 움직인다.
2) CPU는 ‘명령어 사이클’이라는 정해진 흐름에 맞춰 명령어들을 실행한다.
클럭 속도가 높은 CPU는 일반적으로 성능이 좋기 때문에 클럭 속도는 CPU 속도 단위로 간주되기도 한다. 클럭 속도
는 헤르츠(Hz) 단위로 측정한다. 이는 1초에 클럭이 몇 번 반복되는지를 나타낸다.
최대 클럭 속도를 강제로 더 끌어올리는 오버클럭킹(overclocking)과 같은 기법을 사용하여 클럭 속도를 높일 수는 있지만 발열 문제가 심해진다. 때문에 클럭 속도만으로 CPU의 성능을 올리는 것에는 한계가 있다.
클럭 속도를 높이는 방법 외에 CPU의 성능을 높이는 방법에는 CPU의 코어와 스레드 수를 늘리는 방법이 있다.
CPU의 정의로 알고 있었던 ‘명령어를 실행하는 부품’은 오늘날 코어(core)
라는 용어로 사용된다. 다시 말해, 오늘날의 CPU는 단순히 ‘명령어를 실행하는 부품’에서 ‘명령어를 실행하는 부품을 여러 개 포함하는 부품’으로 명칭의 범위가 확장되었다.
코어를 여러 개 포함하고 있는 CPU를 멀티코어(multi-core) CPU
또는 멀티코어 프로세서
라고 부른다.
코어의 개수와 성능이 정비례한 것은 아니다. 코어마다 처리할 연산이 적절히 분배되지 않는다면 코어 수에 비례하여 연산 속도가 증가하지 않는다. 또한 처리하고자 하는 작업량보다 코어 수가 지나치게 많아도 성능에는 크게 영향이 없다. 중요한 것은 코어마다 처리할 명령어들을 얼마나 적절하게 분배하느냐이고, 그에 따라서 연산 속도는 크게 달라진다.
스레드(thread)의 사전적 의미는 ‘실행 흐름의 단위’이지만 용례에 따라 다시 구분된다. 스레드에는 CPU에서 사용되는 하드웨어적 스레드
가 있고, 프로그램에서 사용되는 소프트웨어적 스레드
가 있다.
스레드를 하드웨어적으로 정의하면 ‘하나의 코어가 동시에 처리하는 명령어 단위’를 의미한다. 하나의 코어로 여러 명령어를 동시에 처리하는 CPU를 멀티스레드(multithread) 프로세서
또는 멀티스레드 CPU
라고 한다.
하이퍼스레딩(hyper-threading)
이라는 용어는 인텔이 자신들의 멀티스레드 기술에 부여한 명칭이다.
소프트웨어적으로 정의된 스레드는 ‘하나의 프로그램에서 독립적으로 실행되는 단위’를 의미한다. 한 번에 하나씩 명령어를 처리하는 1코어 1스레드 CPU도 소프트웨어적 스레드를 수십 개 실행할 수 있다.
멀티스레드 프로세서
를 실제로 설계하는 일은 매우 복잡하지만, 가장 큰 핵심은 레지스터이다. 프로그램 카운터, 스택 포인터, 메모리 버퍼 레지스터, 메모리 주소 레지스터와 같이 하나의 명령어를 처리하기 위해 꼭 필요한 레지스터를 여러 개 가지고 있으면 된다.
2코어 4스레드 CPU는 한 번에 네 개의 명령어를 처리할 수 있는데, 프로그램 입자에서 봤을 땐 한 번에 하나의 명령어를 처리하는 CPU가 네 개 있는 것처럼 보인다. 그래서 하드웨어 스레드를 논리 프로세서(logical processor)
라고 부르기도 한다.
위의 내용들을 정리하자면 다음과 같다.
코어
: 명령어를 실행할 수 있는 ‘하드웨어 부품’스레드
: 명령어를 실행하는 단위멀티코어 프로세서
: 명령어를 실행할 수 있는 하드웨어 부품(코어)을 두 개 이상 가지고 있는 CPU멀티스레드 프로세서
: 하나의 코어로 여러 개의 명령어를 동시에 실행할 수 있는 CPU빠른 CPU를 만들기 위해서는 높은 클럭 속도와 멀티코어, 멀티스레드도 중요하지만, CPU가 놀지 않고 시간을 알뜰하게 쓰며 작동하게 만드는 것도 중요하다.
명령어 파이프라인을 이해하려면 하나의 명령어가 처리되는 전체 과정을 비슷한 시간 간격으로 나누어 보아야 한다. 명령어 처리 과정을 클럭 단위로 나누어 보면 일반적으로 다음과 같다.
여기서 중요한 점은 단계가 겹치지만 않는다면 CPU는 ‘각 단계를 동시에 실행할 수 있다’는 것이다. 명령어를 겹쳐서 수행하면 명령어를 하나하나 실행하는 것보다 훨씬 더 효율적으로 처리할 수 있다.
이처럼 마치 공장 생산 라인과 같이 명령어들을 명령어 파이프라인(instruction pipeline)
에 넣고 동시에 처리하는 기법을 명령어 파이프라이닝(instruction pipelining)
이라고 한다.
파이프라이닝이 높은 성능을 가져오기는 하지만, 특정 상황에서는 성능 향상에 실패하는 경우도 있다. 이러한 상황을 파이프라인 위험(pipeline hazzard)
라고 부른다. 파이프라인 위험에는 크게 데이터 위험, 제어 위험, 구조적 위험이 있다.
data hazzard
데이터 위험
이라고 한다.control hazzard
분기 예측(branch prediction)
이라는 기술을 사용한다.CPU 내부에 여러 개의 명령어 파이프라인을 포함한 구조를 슈퍼스칼라(supersclar)
라고 한다. 슈퍼스칼라 구조로 명령어 처리가 가능한 CPU를 슈퍼스칼라 프로세서
또는 슈퍼스칼라 CPU
라고 한다. 슈퍼스칼라 프로세서는 매 클럭 주기마다 동시에 여러 명령어를 인출할 수도, 실행할 수도 있어야 한다. 그래서 멀티스레드 프로세서는 슈퍼스칼라 구조를 사용할 수 있다.
슈퍼스카랄 프로세서는 이론적으로 파이프라인 개수에 비례하여 프로그램 처리 속도가 빨라지지만, 파이프라인 위험 등의 예상치 못한 문제가 있어 실제로는 그렇지 않다. 때문에 이 방식을 차용한 CPU는 파이프라인 위험을 방지하기 위해 고도로 설계되어야 한다. 여러 개의 파이프라인을 이용하면 하나의 파이프라인을 이용할 때보다 데이터 위험, 제어 위험, 자원 위험을 피하기가 더욱 까다롭기 때문이다.
오늘날 CPU 성능 향상에 크게 기여한 기법이자 대부분의 CPU가 차용하는 기법이다. 위에서 설명했던 명령어 파이프라이닝, 슈퍼스칼라 기법은 모두 여러 명령어의 순차적인 처리를 상정한 기법이었다. 파이프라인 위험과 같은 예상치 못한 문제들로 인해 이따금씩 명령어는 곧바로 처리되지 못하기도 하며 명령어 파이프라인이 멈춰버리게 된다.
위와 같은 문제를 해결하고 순차적인 명령어 처리보다 효율적으로 처리되는, 명령어를 순차적으로만 실행하지 않고 순서를 바꿔 실행해도 무방한 명령어를 먼저 실행하여 명령어 파이프라인이 멈추는 것을 방지하는 기법을 비순차적 명령어 처리 기법(OoOE; Out-of-order-execution)
이라고 한다.
비순차적 명령어 처리가 가능한 CPU는 명령어들이 어떤 명령어와 데이터 의존성을 가지고 있는지, 순서를 바꿔 실행할 수 있는 명령어에는 어떤 것들이 있는지를 판단할 수 있어야 한다.
명령어 파이프라이닝과 슈퍼스칼라 기법을 실제로 CPU에 적용하려면 명령어가 파이프라이닝에 최적화되어 있어야 한다.
→ CPU가 인출하고 해석하고 실행하는 명령어가 파이프라이닝 하기 쉽게 생겨야 한다.
CPU가 이해할 수 있는 명령어들의 모음을 명령어 집합(instrcution set)
또는 명령어 집합 구조(ISA; Instruction Set Architecture)
라고 한다.
CPU마다 ISA가 다를 수 있다. 같은 소스 코드로 만들어진 같은 프로그램이라 할지라도 ISA가 다르면 CPU가 이해할 수 있는 명령어도 어셈블리어도 달라진다.
ISA가 다르면 나비 효과로 제어장치가 명령어를 해석하는 방식, 사용되는 레지스터의 종류와 개수, 메모리 관리 방법 등 많은 것이 달라진다. 이는 곧 CPU 하드웨어 설계에도 큰 영향을 미친다.
CISC(Complex Instruction Set Computer)
복잡하고 다양한 명령어들을 활용하는 CPU 설계 방식이다. 명령어의 형태와 크기가 다양한 가변 길이 명령어
를 활용하고 아주 특별한 상황에서만 사용되는 독특한 주소 지정 방식들도 있다.
다양하고 강력한 명령어를 활용한다는 것은 상대적으로 적은 수의 명령어로도 프로그램을 실행할 수 있음을 의미한다. 이런 장점 덕분에 메모리를 최대한 아끼며 개발해야 했던 시절에 인기가 높았다. 메모리 공간을 절약할 수 있다는 장점이기 때문이다.
그러나 활용하는 명령어가 워낙 복잡하고 다양한 기능을 제공하는 탓에 명령어의 크기와 실행되기 까지의 시간이 일정하지 않다. 복잡한 명령어 때문에 명령어 하나를 실행하는 데에 여러 클럭 주기를 필요로 한다. 이는 명령어 파이프라인을 구현하는데 큰 걸림돌이 된다.
정리하면, CISC 명령어 집합은 복잡하고 다양한 기능을 제공하기에 적은 수의 명령으로 프로그램을 동작시키고 메모리를 절약할 수 있지만, 명령어의 규격화가 어려워 파이프라이닝이 어렵다. 그리고 대다수의 복잡한 명령어는 그 사용 빈도가 낮다. 이러한 이유로 CISC 기반 CPU는 성장에 한계가 있다.
CISC를 통해 얻은 교훈: ‘명령어 길이와 수행 시간이 짧고 규격화’, ‘자주 쓰이는 기본적인 명령어를 작고 빠르게 만들 것’
RISC(Reduced Instruction Set Computer)
고정 길이 명령어
를 활용한다.명령어가 규격화되어 있고, 하나의 명령어가 1클럭 내외로 실행되기 때문에 RISC 명령어 집합은 명령어 파이프라이닝에 최적화되어 있다.
RISC는 메모리 접근을 단순화하고 최소화를 추구한다. 대신 레지스터를 적극적으로 활용한다. 때문에 CISC보다 레지스터를 이용하는 연산이 많고, 일반적인 경우보다 범용 레지스터 개수도 많다.
사용 가능한 명령어 개수가 CISC보다는 적기 때문에 CISC보다 많은 명령으로 프로그램을 작동시킨다.
RISC는 메모리에 직접 접근하는 명령어를 load, store 두 개로 제한한다. 이러한 점 때문에 RISC를 load-store 구조라고도 부른다.