안녕하세요. IT 엘도라도 에 오신 것을 환영합니다.
글을 쓰는 것은 귀찮지만 다시 찾아보는 것은 더 귀찮습니다.
완전한 나만의 것으로 만들기 위해 지식을 차곡차곡 저장해 보아요.   포스팅 둘러보기 ▼

컴퓨터 구조 (Architecture)/CSAPP

[CSAPP] Overview

피그브라더 2020. 2. 24. 23:21

1. Instruction Set Architecture (ISA)

1-1. 인터페이스 (Interface)

인터페이스의 사전적 정의는 '독립적이면서 때로는 서로 관련이 없는 두 시스템이 접촉하여 서로 통신을 하는 지점'이다. 예를 들어 인간이라는 시스템과 자동차라는 시스템 두 개를 생각해 보자. 두 시스템은 당연히 서로 관련이 없고 독립적이다. 그러나 인간이 자동차를 운전하려고 할 때, 두 시스템은 접촉하게 된다. 이때 인간이라는 시스템이 자동차라는 시스템을 원하는 방식대로 조작하기 위해서는 핸들, 브레이크, 엑셀 등과 같이 두 시스템 간의 인터페이스가 필요하다. 여기서 중요한 점은, 인간은 자동차의 내부 구현까지 알 필요가 없다는 것이다. 예를 들어 핸들을 오른쪽으로 돌리면 자동차가 오른쪽으로 간다는 것만 알면 되지, 내부적으로 무슨 일이 일어나서 그렇게 되는지는 알 필요 없다. 이렇듯 인터페이스의 핵심은, 두 시스템이 통신하기 위해 필요한 정보만 최소한으로 드러내고 나머지는 숨기는 것이다. 불필요한 정보는 숨김으로써 두 시스템이 더 효율적으로 통신할 수 있게 된다.


1-2. 추상 자료형 (Abstract Data Type, ADT)

추상 자료형이란 '특정 데이터들로 표현된 상태들, 그리고 그 상태들을 조작할 수 있는 연산들의 집합'을 의미하며, '추상'이라는 표현이 나타내듯 그러한 상태들과 연산들이 내부적으로 구현되는 방식은 무엇이든 상관이 없다. 즉 내부 구현을 추상화한다는 것이다.

 

예를 들어, 스택(Stack)이 바로 대표적인 추상 자료형이다. 스택의 현재 상태는 가장 밑바닥 주소를 가리키는 포인터, 가장 꼭대기 주소를 가리키는 포인터, 그리고 스택에 차곡차곡 쌓여있는 데이터들로 나타낼 수 있다. 그리고 그러한 스택의 현재 상태를 조작하기 위한 연산들로는 데이터를 꺼내는 팝(Pop) 연산, 데이터를 쌓는 푸시(Push) 연산 등등이 있을 수 있다. 이렇게 스택이라는 추상 자료형을 정의하고 나면, 그러한 상태들과 연산들이 내부적으로 어떻게 구현되든 상관이 없다.

 

 

그렇다면 추상 자료형을 왜 갑자기 지금 설명하는 것일까? 그것은 바로 추상 자료형이 인터페이스의 역할을 수행하기 때문이다. 누군가 스택의 데이터를 다루기 위해서는, 스택이라는 것의 상태가 어떻게 표현되고 그 상태들을 조작할 수 있는 연산들로는 무엇이 있는지 알아야 한다. 추상 자료형에서 정의하는 내용들이 바로 이것이다. 즉 스택의 데이터를 다루고자 하는 사람은, 스택의 데이터가 접시처럼 쌓여 있는 구조라는 것과, 맨 위의 데이터를 꺼내거나 새로운 데이터를 맨 위에 쌓을 수 있다는 것만 알면 된다. 실제로 데이터가 메모리 상에 접시처럼 쌓여있는 구조인 건지, 맨 위의 데이터를 꺼낼 때 실제로 메모리 상에서는 무슨 변화가 일어나는지 등은 구현하는 사람의 마음이며 사용하는 사람 입장에서 알 필요가 없어서 숨겨놓는 것이다.


1-3. Instruction Set Architecture (ISA)

ISA는 소프트웨어와 하드웨어 사이의 인터페이스이다. 인간이 해당 CPU의 ISA 체계에 맞는 소프트웨어 코드를 작성하면, CPU는 그 코드들을 읽고 명령된 동작들을 올바르게 수행한다. 즉 ISA는 CPU에게 특정 동작을 명령하기 위해 사용자가 알고 있어야 하는 모든 정보들의 집합으로, 일종의 CPU 사용 명세서라고 볼 수 있다. 그러한 측면에서 명백히도 ISA는 CPU와 통신하기 위한 인터페이스인 것이다.

 

 

한편, 더 나아가 ISA는 하나의 추상 자료형으로 바라볼 수도 있다. CPU의 내부 상태를 나타낼 수 있는 레지스터와 메모리의 조직에 대한 것을 정의하고 있고, 레지스터와 메모리의 값을 조작하기 위한 연산들로서 여러 종류의 명령어들을 정의하고 있기 때문이다. 그리고 ISA가 CPU의 내부 구현 방식까지는 정의하고 있지 않다는 것도 그렇다. 하나의 ISA를 사용하는 CPU가 여러 개가 될 수 있는 것도, 한 ISA를 구현하는 방식으로는 여러 가지가 존재하기 때문이다. (참고로, Introduction to Computing Systems 포스팅에서 다루는 ISA인 LC-3의 CPU는 Sequential 방식으로 구현이 된다. CPU를 구현하는 방식에 대해서는 뒤에서 차차 알아볼 것이다.)

 

 

예를 들어, 이해를 돕기 위해 아주 간단한 ISA를 하나 상상해 보자. 워드 사이즈는 8비트이고, 레지스터로는 범용 레지스터 4개와 PC가 있고, 메모리의 크기는 총 64바이트이다. 다음 그림들은 해당 ISA가 정의하는 연산(명령어)에 의해 상태(레지스터, 메모리)가 변화하는 모습을 시각적으로 보여준다.

 

 

우리는 이제 앞으로 64비트 인텔 CPU의 ISA에 해당하는 x86-64에 대해 차근차근 알아볼 것이다. 이는 임베디드 시스템에서 많이 채택되는 ARM 사의 ISA와 함께 세계적으로 가장 많이 사용되는 ISA라고 할 수 있다.

 

2. 디자인 기법 (Design Technique)


2-1. 프로세서 (Processor)

프로세서, 즉 CPU를 구현하는 방식은 명령어(Instruction) 실행 방식의 측면에서 다음과 같이 대략 네 가지로 구분할 수 있다. 이 중에서 우리가 다룰 중요한 내용은 Sequential Implementation과 Pipelined Implementation이다. 이 두 방식에 대한 설명은 추후 포스팅에서 아주 자세하게 다룰 예정이므로 여기서는 간단하게만 읽고 넘어가자. 참고로 밑으로 갈수록 CPU의 성능(속도)은 향상되지만, 올바른 동작인가에 대한 의문은 더 커진다. 성능을 높이기 위한 복잡한 구현은 버그를 만들어낼 가능성도 크기 때문이다. 일반적으로 99.9%에서 올바른 동작을 보장하는 것은 어려운 일이 아니지만, 0.1% 이하의 사소한 경우들에서도 올바른 동작을 보장하는 것은 굉장히 어려운 법이다.

 


2-2. 캐시 메모리 (Cache Memory)

일반적으로 우리가 RAM이라고 부르는 메인 메모리는 DRAM(Dynamic RAM)으로, 캐시 메모리는 SRAM(Static RAM)으로 만들어진다. DRAM은 상대적으로 속도가 느리지만 단위 용량 가격이 저렴하기 때문에 용량이 큰 메인 메모리를 만들 때 사용이 되고, SRAM은 속도가 빠르지만 상대적으로 가격이 비싸기 때문에 용량이 적은 캐시 메모리를 만들 때 사용이 되는 것이다.

 

캐시 메모리의 역할은 무엇일까? SRAM으로 만들어지는 캐시 메모리는 메인 메모리보다 접근 속도가 아주 빠르기 때문에, CPU 입장에서는 찾고자 하는 데이터가 메인 메모리가 아닌 캐시 메모리에 있는 편이 훨씬 좋다. 이러한 원리에 근거하여, CPU는 메모리에 접근하고자 할 때 우선 캐시 메모리부터 확인한다. 만약 찾고자 하는 데이터가 캐시 메모리에서 발견이 되면 Hit, 그렇지 않으면 Miss라고 한다. 그렇다면 캐시 Hit 확률은 얼마나 될까? 놀랍게도, 특정한 아이디어에 근거하여 자주 참조될 것으로 예상되는 데이터들을 캐시 메모리에 저장하는 현대의 CPU들은 캐시 Hit 확률이 무려 95%가 넘는다. 이렇게 높은 확률로 캐시 Hit를 발생시킬 수 있는 아이디어는 바로 지역성(Locality)이다. 지역성은 다음과 같이 두 가지로 나눌 수 있다.

 

종류 의미
Temporal Locality 어떤 데이터를 참조하면, 가까운 미래에 그 데이터를 다시 참조할 확률이 높다.
Spatial Locality 어떤 데이터를 참조하면, 가까운 미래에 그 주변의 데이터를 참조할 확률이 높다.

 

예를 들어, C 언어의 for 문에서 카운터 변수에 해당하는 i는 반복적으로 참조가 된다. 이러한 경우 변수 i를 캐시 메모리에 두면 처리 속도가 상당히 빨라질 것이다. 이것이 바로 Temporal Locality이다. 또한, C 언어의 배열 내 요소들은 순차적으로 접근하는 경우가 많다. 따라서 배열 내 요소들을 뭉치로 캐시 메모리에 저장해 두면 처리 속도가 상당히 빨라질 것이다. 이것이 바로 Spatial Locality이다.

 

이 원리를 캐시 메모리에 적용하여 생각해 보자. 메인 메모리는 기본적으로 블록(8~128 바이트) 단위로 쪼개진다. 이후 어떤 데이터의 참조에서 Miss가 발생하면 그 데이터를 포함하는 메인 메모리의 한 블록을 캐시 메모리에 저장한다. 이는 Spatial Locality를 활용한 것이다. 또한 캐시 메모리 내 블록들을 최근에 참조된 순서대로 정렬하고, 캐시 메모리가 꽉 차서 특정 블록을 버려야 하는 경우 가장 예전에 참조되었던 블록을 버리도록 한다. 이는 Temporal Locality를 활용한 것이다.

 

3. 공학 방법론 (Engineering Methodology)

공학자로서 시스템을 설계할 때 가장 기본적으로 지켜야 할 규칙을 살펴보자.

 

3-1. 흔한 경우를 빠르게 하라 : Make the (many) common cases fast.

몇 없는 경우에 대해서 속도 향상을 도모해봤자 전체적으로는 성능이 그리 크게 향상되지 않는다. 반면에, 자주 수행되는 작업에 대해 속도 향상을 도모하면 전체적인 성능도 눈에 띄게 좋아진다.

 

3-2. 드문 경우도 되도록 하라 : Make the (a few) rare cases correct.

거의 일어나지 않는 경우라고 해서 긴장의 끈을 놓으면 안 된다. 미리 모든 경우를 파악하여, 어떤 경우에라도 버그가 발생하지 않고 올바르게 동작하도록 꼼꼼하고 세심하게 디자인하는 자세가 필요하다.

 

4. 정확성의 기준 (Correctness Criteria) - as if's

그렇다면 위에서 말한 '올바르게 동작'한다는 것은 무엇을 의미할까? 간단하다. 가장 단순하고 무식하지만 모든 경우에 대해 올바르게 동작하는 방식으로 디자인했을 때와 결과(성능이 아니라)가 완전히 동일하면 된다.

 

예를 들어, CPU의 명령어 실행 구현 방식 중 가장 단순하면서 정확한 것은 Sequential Implementation이다. 따라서 성능을 높이기 위해 Pipelined Implementation을 구현하였다면 명령어 실행 결과가 모든 경우에 대해서 Sequential Implementation과 같은지 보면 된다. 또한, 메모리 계층 구현 방식 중 가장 단순하면서 정확한 것은 당연하게도 메인 메모리만 딱 하나 존재하는 구현 방식이다. 따라서 캐시 메모리가 존재하는 방식을 구현하였다면 메모리 접근 결과가 모든 경우에 대해서 메인 메모리가 하나만 있을 때와 같은지 보면 된다.

 

5. 성능 평가 방법 (Performance Evaluation Method)

우리가 디자인한 구현 방식의 성능을 평가하는 척도로 몇 가지만 소개하도록 한다. 자세한 내용은 추후 포스팅에서 다룰 예정이다.

 

첫째는 Time, 즉 처리 시간과 관련된 척도이다. 요청한 작업이 처리될 때까지 걸리는 시간을 측정한다. 이에 해당하는 평가 척도로는 응답 시간(Response Time), 실행 시간(Execution Time) 등이 있다. 둘째는 Rate, 즉 처리 속도와 관련된 척도이다. 단위 시간 동안 처리하는 데이터의 양을 측정한다. 이에 해당하는 평가 척도로는 MIPS, MFLOPS 단위를 사용하는 처리량(Throughput), Mbps 단위를 사용하는 대역폭(Bandwidth) 등이 있다. 마지막은 Ratio로, Time과 Rate를 동시에 고려하여 상대적인 성능을 평가하는 척도를 의미한다.

'컴퓨터 구조 (Architecture) > CSAPP' 카테고리의 다른 글

[CSAPP] x86-64 - Procedures  (2) 2020.03.01
[CSAPP] x86-64 - Control  (7) 2020.02.29
[CSAPP] x86-64 - Basics  (4) 2020.02.28
[CSAPP] Virtualization  (0) 2020.02.26
[CSAPP] 포스팅을 시작하며..  (4) 2020.02.24