활동 내용

Chapter 4 ~ 5, CPU의 구조와 CPU 성능 향상 기법까지

  • 기본 미션: p. 125의 확인 문제 2번, p. 155의 확인 문제 4번 풀고 인증하기
  • 선택 미션: Ch.05(05-1) 코어와 스레드, 멀티 코어와 멀티 스레드의 개념을 정리하기

정리

ALU


위의 그림은 CPU 내부에서 계산을 담당하는 ALU가 어떤 정보를 받아들이고 내보내는지를 표현한 것이다. 먼저 ALU는 레지스터로부터 피연산자를 받아들이고, 제어장치로부터 제어 신호를 받아들인다. 그 후 수행 결과값을 레지스터에 저장한다. 이 과정에서 연산 결과에 대한 추가적인 상태 정보가 있다면 플래그 레지스터에 플래그를 보낸다.
플래그란 연산 결과에 대한 추가적인 상태 정보이며, ALU가 보내는 대표적인 플래그는 아래와 같다.

플래그 종류의미
부호 플래그연산한 결과의 부호를 나타낸다. 플래그가 1이면 음수, 0이면 양수이다.
제로 플래그연산 결과가 0인지 여부를 나타낸다. 플래그가 1이면 0, 0이면 0이 아니다.
캐리 플래그연산 결과 올림수나 빌림수가 발생했는지를 나타낸다. 플래그가 1이면 발생한 것이며, 0이면 아니다.
오버플로우 플래그오버플로우가 발생했는지를 나타낸다. 플래그가 1이면 발생한 것이며, 0이면 아니다.
인터럽트 플래그인터럽트가 가능한지를 나타낸다. 플래그가 1이면 가능하고, 0이면 아니다.
슈퍼바이저 플래그플래그가 1이면 커널 모드로 실행 중임을 의미하고, 0이면 사용자 모드로 실행 중임을 의미한다.

제어장치


제어장치는 클럭, 현재 수행할 명령어, 플래그, 제어 신호를 받아들이며, CPU 내부와 외부로 제어 신호를 보낸다.
위 그림에서 주목할 점은 클럭이다. 클럭(clock)이란 컴퓨터 모든 부품의 행동 시간 단위이다. 클럭의 주기에 맞춰 한 레지스터에서 다른 레지스터로 데이터를 이동하거나, CPU가 메모리에 저장된 명령어를 읽어들인다.

레지스터

상용화된 CPU 속 레지스터들은 CPU마다 종류가 매우 다양하다. 그 중 공통으로 포함하고 있는 레지스터는 8개 정도 있다.

  • 프로그램 카운터(PC, Program Counter): 메모리에서 읽어 들일 명령어 주소를 저장한다. 명령어 포인터(instruction pointer)라고 부르기도 한다.
  • 명령어 레지스터(IR, Instruction Register): 메모리에서 읽어 들인 명령어를 저장하는 레지스터이다.
  • 메모리 주소 레지스터(MAR, Memory Address Register): 메모리의 주소를 저장하는 레지스터이다. CPU가 읽어 들이고자 하는 주소 값을 주소 버스로 보낼 때 메모리 주소 레지스터를 거친다.
  • 메모리 버퍼 레지스터(MBR, Memory Buffer Register): 메모리와 주고받을 값(데이터와 명령어)을 저장하는 레지스터이다. 메모리 데이터 레지스터(MDR, Memory Data Register)라고도 부른다.
  • 범용 레지스터(general purpose register): 데이터와 주소를 모두 저장할 수 있는 레지스터이다. 일반적인 상황에서 자유롭게 사용할 수 있다.
  • 플래그 레지스터(flag register): 연산 결과 또는 CPU 상태에 대한 부가적인 정보를 저장하는 레지스터이다.
  • 스택 포인터(stack pointer): 스택 주소 지정 방식에 사용되는 레지스터이다. 스택의 꼭대기를 가리킨다. 즉, 스택에 마지막으로 저장한 값의 위치를 저장하는 레지스터이다.
  • 베이스 레지스터(base register): 베이스 레지스터 주소 지정 방식에 사용되는 레지스터이다. 베이스 레지스터에 저장된 주소는 기준 주소로서의 역할을 한다. (아래에 추가 설명있음)

특정 레지스터를 이용한 주소 지정 방식

  • 변위 주소 지정 방식(displacement addressing mode): 오퍼랜드 필드의 값과 특정 레지스터의 값을 더하여 유효 주소를 얻어내는 주소 지정 방식이다. 이때, 어떤 레지스터를 더하는지에 따라 상대 주소 지정 방식, 베이스 레지스터 주소 지정 방식 등으로 나뉜다.
  • 상대 주소 지정 방식(relative addressing mode): 오퍼랜드와 프로그램 카운터의 값을 더하여 유효 주소는 얻는 방식이다.
    예를 들어 오퍼랜드가 3이라면 CPU는 읽어 들이기로 했던 명령어의 '세 번째 이후' 번지로 접근한다.
  • 베이즈 레지스터 주소 지정 방식(base-register addressing mode): 오퍼랜드와 베이스 레지스터의 값을 더하여 유효 주소를 얻는 방식이다. 여기서 베이스 레지스터는 '기준 주소', 오퍼랜드는 '기준 주소로부터 떨어진 거리'로서의 역할을 한다.
    예를 들어 베이즈 레지스터에 100이라는 값이 있고 오퍼랜드가 40이라면 "기준 주소 100번지로부터 40만큼 떨어진 140번지로 접근하라"를 의미한다.

명령어 사이클

프로그램 속 각각의 명령어들은 일정한 주기가 반복되며 실행되는데, 이 주기를 명령어 사이클(instruction cycle)이라고 한다. 명령어 사이클은 총 3단계로 구분되어 있다. 첫 번째 과정은 메모리에 있는 명령어를 CPU로 가지고 오는 단계인 인출 사이클(fetch cycle)이다. 두 번째 과정은 CPU로 가져온 명령어를 실행하는 단계인 실행 사이클(execution cycle)이다. 프로그램을 이루는 수많은 명령어는 일반적으로 인출과 실행 사이클을 반복하며 실행된다.
그러나 간접 주소 지정 방식과 같이 명령어를 실행하기 위해 메모리 접근을 한 번 더 해야 하는 과정도 있다. 이 단계를 간접 사이클(indirect cycle)이라고 한다.

인터럽트

CPU는 정해진 흐름에 따라 명령어를 처리해 나가지만, 간혹 이 흐름이 끊기는 상황이 발생한다. 이를 인터럽트(interrupt)라고 한다. 인터럽트는 동기 인터럽트와 비동기 인터럽트로 나뉜다.
동기 인터럽트(synchronous interrupt)는 CPU가 명령어들을 수행하다가 예기치 못한 상황에 마주쳤을 때 발생하는 인터럽트이다. 예외(exception)라고 부르기도 한다.
비동기 인터럽트(asynchronous interrupt)는 주로 입출력 장치에 의해 발생하는 인터럽트이다. 하드웨어 인터럽트라고 부르기도 한다.

하드웨어 인터럽트 처리 순서

  1. 입출력장치는 CPU에 인터럽트 요청 신호를 보낸다.
  2. CPU는 실행 사이클이 끝나고 명령어를 인출하기 전 항상 인터럽트 여부를 확인한다.
  3. CPU는 인터럽트 요청을 확인하고 인터럽트 플래그를 통해 현재 인터럽트를 받아들일 수 있는지 여부를 확인한다.
  4. 인터럽트를 받아들일 수 있다면 CPU는 지금까지의 작업을 백업한다.
  5. CPU는 인터럽트 벡터를 참조하여 인터럽트 서비스 루틴을 실행한다.
  6. 인터럽트 서비스 루틴 실행이 끝나면 백업해 둔 작업을 복구하여 실행을 재개한다.

여기서 주목할 점은 인터럽트 벡터와 인터럽트 서비스 루틴이다. 인터럽트 벡터(interrupt vector)는 인터럽트 서비스 루틴을 식별하기 위한 정보이다. 인터럽트 벡터를 알면 인터럽트 서비스 루틴의 시작 주소를 알수 있기에 CPU는 데이터 버스를 통해 인터럽트 벡터를 전달받아 특정 인터럽트 서비스 루틴을 실행한다.
인터럽트 서비스 루틴(ISR, Interrupt Service Router)은 인터럽트를 처리하기 위한 프로그램이다. '키보드가 어떤 인터럽트 요청을 보냈을 때는 어떻게 작동한다', '마우스가 어떤 인터럽트 요청을 보냈을 때는 어떻게 작동한다'와 같이 어떤 인터럽트가 발생했을 때 해당 인터럽트를 어떻게 처리하고 작동해야 할지에 대한 정보로 이루어진 프로그램이다. 인터럽트 핸들러(interrupt handler)라고도 부른다.
이 외에도 인터럽트 요청 신호인터럽트 플래그도 중요한 키워드이다. 특히나 인터럽트 플래그는 모든 하드웨어 인터럽트를 막을 수 있는 것이 아니다. 인터럽트 플래그를 불가능으로 설정해도 무시할 수 있는 인터럽트 요청도 있다. 대표적인 예시로 정전이나 하드웨어 고장으로 인한 인터럽트가 해당된다.

클럭

클럭(clock)이란 컴퓨터 모든 부품의 행동 시간 단위이다. 클럭 신호가 빠르게 반복되면 CPU를 비롯한 컴퓨터 부품들은 그만큼 빠른 박자에 맞춰 움직이기에 클럭 속도는 CPU 속도 단위로 간주되기도 한다.
클럭 속도는 헤르츠(Hz) 단위로 측정한다. 가령 클럭에 1초에 한 번 반복되면 CPU 클럭 속도는 1Hz인 것이다. 또한 클럭 속도는 일정하지 않고 고성능을 요하는 순간에는 순간적으로 클럭 속도를 높이고, 그렇지 않을 때는 유연하게 클럭 속도를 낮추기도 한다. 최대 클럭 속도를 강제로 더 끌어올릴 수도 있는데, 이런 기법을 오버클럭킹(overclocking)이라고 한다.

코어와 멀티코어

코어*(core)는 CPU의 정의로 알고 있던 '명령어를 실행하는 부품'이다. 오늘날 CPU는 단순히 '명령어를 실행하는 부품'에서 '명령어를 실행하는 부품을 여러 개 포함하는 부품'으로 명칭의 범위가 확장되었다.
그렇기에 콩를 여러 개 포함하고 있는 CPU를 멀티코어 CPU(multi-core CPU) 또는 멀리코어 프로세서라고 부른다.

스레드와 멀티스레드

스레드(thread)의 사전적 의미는 '실행 흐름의 단위'이다. 스레드에는 CPU에서 사용되는 하드웨어적 스레드가 있고, 프로그램에서 사용되는 소프트웨어적 스레드가 있다.
하드웨어적 스레드는 '하나의 코어가 동시에 처리하는 명령어 단위'를 의미한다. 그리고 CPU에선 여러 명령어를 동시에 처리하는 CPU도 있는데, 이를 멀티 스레드 프로세서(multithread) 또는 멀티스레드 CPU라고 한다. 예를 들어 2코어 4스레드 CPU는 명령어를 실행하는 부품을 두 개 포함하고, 한 번에 네 개의 명령어를 처리할 수 있는 CPU를 의미한다.
멀티스레드 프로세서의 핵심은 레지스터이다. 하나의 코어로 여러 명령어를 동시에 처리하도록 만들려면 하나의 명령어를 처리하기 위해 꼭 필요한 레지스터를 여러 개 가지고 있으면 된다. 이렇듯 하드웨어적 스레드를 이용해 하나의 코어로도 여러 명령어를 동시에 처리할 수 있다. 그러나 메모리 속 프로그램 입장에서 봤을 때 하드웨어적 스레드는 마치 '한 번에 하나의 명령어를 처리하는 CPU'나 다름없다. 예를 들어 2코어 4스레드는 프로그램 입장에서 CPU가 4개인 것처럼 보인다. 그래서 하드웨어적 스레드를 논리 프로세서(logical processor)라고 부르기도 한다.
소프트웨어적 스레드는 '하나의 프로그램에서 독립적으로 실행되는 단위'를 의미한다. 하드웨어적 스레드와 구분하여 기억해야 하며, Python, Java, C++ 등의 프로그래밍 언어를 이용해 소프트웨어적 스레드를 만들 수 있다.

명령어 파이프라인

명령어 병렬 처리 기법인 명령어 파이프라인을 이해하려면 하나의 명령어가 처리되는 전체 과정을 비슷한 시간 간격으로 나누어 보아야 한다. 명령어 처리 과정을 클럭 단위로 나누어 보면 다음과 같다.

  1. 명령어 인출
  2. 명령어 실행

여기서 중요한 점은 같은 단계가 겹치지만 않는다면 CPU는 각 단계를 동시에 실행할 수 있다. 마치 공장 생산 라인과 같이 명령어들을 명령어 파이프라인(instruction pipeline)에 넣고 동시에 처리하는 기법을 명령어 파이프라이닝(instruction pipelining)이라고 한다.
명령어 파이프라이닝은 높은 성능을 가져온다는 장점이 있지만, 성능 향상에 실패하는 경우도 있다. 이러한 상황을 파이프라인 위험(pipeline hazard)이라고 부른다. 파이프라인 위험은 크게 아래와 같다.

  • 데이터 위험(data hazard): 명령어 간 데이터 의존성에 의해 발생한다.
명령어 1: R1 <- R2 + R3
명령어 2: R4 <- R1 + R5

위의 경우 명령어 1을 수행해야만 명령어 2를 수행할 수 있다. 이처럼 데이터 의존적인 두 명령어를 무작정 동시에 실행하려고 하면 파이프라인이 제대로 작동하지 않는다.

  • 제어 위험(control hazard): 주로 분기 등으로 인한 '프로그램 카운터의 갑작스러운 변화'에 의해 발생한다. 예를 들어 프로그램 실행 중 프로그램 카운터 값에 갑작스러운 변화가 생긴다면 명령어 파이프라인에 미리 가지고 와서 처리 중이었던 명령어들은 아무 쓸모가 없어진다. 오늘날 이를 위해 분기 예측(branch prediction) 기술을 사용하기도 한다.
  • 구조적 위험(structural hazard): 서로 다른 명령어가 동시에 ALU 레지스터 등과 같은 CPU 부품을 사용하려고 할 때 발생한다. 구조적 위험은 자원 위험(resource hazard)이라고도 부른다.

슈퍼스칼라

슈퍼 스칼라(superscalar)는 CPU 내부에 여러 개의 명령어 파이프라인을 포함한 구조이다. 슈퍼스칼라 구조로 명령어 처리가 가능한 CPU를 슈퍼스칼라 프로세서 또는 슈퍼스칼라 CPU라고 한다.

비순차적 명령어 처리

비순차적 명령어 처리(OoOE, Out-of-order execution)는 명령어들을 순차적으로만 실행하지 않고 순서를 바꿔 실행해도 무방한 명령어를 먼저 실행하여 명령어 파이프라인이 멈추는 것을 방지하는 기법이다. 명령어의 '합법적인 새치기'라고 볼 수 있다.

명령어 집합 (ISA)

CPU가 이해할 수 있는 명령어들의 모음을 명령어 집합(instruction set) 또는 명령어 집합 구조(ISA, Instruction Set Architecture)라고 하며, 이하 ISA라고 한다. CPU의 종류에 따라 ISA는 다르다. 이는 CPU가 이해할 수 있는 명령어가 각각 다르다는 뜻이고, 명령어가 달라지면 어셈블리어도 달라진다. 이런 점에서 볼 때 ISA는 일종의 CPU의 언어임과 동시에 CPU를 비롯한 하드웨어가 소프트웨어를 어떻게 이해할지에 대한 약속이라고도 볼 수 있다.

CISC와 RISC

CISC(Complex Instruction Set Computer)는 '복잡한 명령어 집합을 활용하는 컴퓨터'를 의미한다. 가변 길이 명령어를 활용하며, 상대적으로 적은 수의 명령어로도 프로그램을 실행할 수 있다. 그러나 활용하는 명령억가 워낙 복잡하고 다양한 기능을 제공하는 탓에 명령어의 크기와 실행되기까지의 시간이 일정하지 않다. 이는 명령어 파이프라인을 구현하는 데에 큰 걸림돌이 된다
이를 개선하여 나온 것이 RISC(Reduced Instruction Set Computer)이다. RISC는 고정 길이 명령어를 활용하며 아래의 원칙 하에 만들어졌다.

  1. 빠른 처리를 위해 명령어 파이프라인을 활용한다. 그렇기에 명령어 길이와 수행 시간이 짧고 규격화되어 있어야 한다.
  2. 복잡한 기능을 지원하는 명령어를 추가하기보다 자주 쓰이는 기본적인 명령어를 작고 빠르게 만드는 것이 중요하다.

이렇게 각기 다른 성격의 ISA를 기반으로 만들어진 CPU 설계 방식이 CISC와 RISC이다. 아래는 CISC와 RISC의 차이를 정리한 것이다.

CISCRISC
복잡하고 다양한 명령어단순하고 적은 명령어
가변 길이 명령어고정 길이 명령어
다양한 주소 지정 방식적은 주소 지정 방식
프로그램을 이루는 명령어의 수가 적음프로그램을 이루는 명령어의 수가 많음
여러 클럭에 걸쳐 명령어 수행1클럭 내외로 명령어 수행
파이프라이닝하기 어려움파이프라이닝하기 쉬움

기본 미션

p. 125의 확인 문제 2번, p. 155의 확인 문제 4번 풀고 인증하기


2. 다음 설명에 맞는 레지스터를 보기에서 찾아 빈칸을 채워 보세요.

- 플래그 레지스터: 연산 결과 혹은 CPU 상태에 대한 부가 정보를 저장하는 레지스터
- 프로그램 카운터: 메모리에서 가져올 명령어의 주소를 저장하는 레지스터
- 범용 레지스터: 데이터와 주소를 모두 저장할 수 있는 레지스터
- 명령어 레지스터: 해석할 명령어를 저장하는 레지스터

4. 다음 그림은 멀티코어 CPU를 간략하게 도식화한 그림입니다. 빈칸에 알맞은 용어를 써 넣으세요.


선택 미션

코어와 스레드, 멀티 코어와 멀티 스레드의 개념을 정리하기


우측 목차에서 코어와 멀티코어, 스레드와 멀티 스레드 참조하시길 바랍니다.
profile
안녕하세요.

0개의 댓글