Jungle

[TIL][Krafton Jungle] 3주차 - (1) : 컴퓨터 시스템 정리 + DP

손가든 2023. 10. 26. 22:46

오늘은 컴퓨터 시스템 공부한 것을 최대한 정리하고, 알고리즘 풀이를 살짝 곁들일 생각이다.

 

CS:APP 책이 영문판을 번역한 번역본이라, 글이 너무 이상하고 낯설게 작성되있다.

최대한 이해하기 쉽게 내 말로 풀어서 작성해 보겠다.

 

 

아무튼 그렇게 3주차가 시작됬다.

 

이번주도 파이팅~

 


컴퓨터 시스템 (CSAPP) 정리

1.5 캐시가 중요하다.

프로세서인 CPU는 컴퓨터의 뇌로 역할을 수행하고, 레지스터와 메인 메모리, 디스크 메모리는 CPU에게 수행 명령을 지시 받는데,

 

파일을 실행 시에 인스트럭션(수행 명령)들이 메인 메모리로부터 CPU로 복사되고,

 

인스트럭션을 읽고 실행을 수행하라는 명령을 내리는 것으로,

디스크 메모리에 있던 파일을 메인 메모리로 복사하고~

(이때, 바로 디스크의 메모리가 프로세서로 거치치 않고 메인 메모리로 바로 복사시키는 기법이, 직접 메모리 접근법이라고 한다.)

 

메인 메모리에 있는 스트링을 디스플레이로 복사시킨다.

 

이 복사 작업은 실제 작업을 느리게 하는 '오버헤드' 이다.

 

따라서 이 오버헤드를 줄이려면 어떻게 해야 하는가?

 

메인 메모리의 성능이 프로세서(CPU 칩)보다 월등히 느리기 때문에 !

메인 메모리로 왔다 갔다 하는 것이 부담이 되는 것이고,

 

레지스터처럼 CPU 가까이있고 속도가 빠른 임시 데이터센터를 만드는 것이다.

그게 바로 '캐시'인 것.

 

레지스터는 메인 메모리보다 용량은 적지만, 메인 메모리의 100배 빠른 성능을 보인다.

메인 메모리는 멀리 있고 속도가 느리기 때문에 메인 메모리보다는 빠르고,

용량은 적은 임시 중간 저장체를 두는 것이다.

 

이 캐시 저장소에는 모든 데이터가 있는 것은 아니고,

단 시간에 필요할 가능성이 높은 정보를 임시로 저장한다.

 

CPU 칩 안(레지스터도 칩 안에 존재)에 들어있는 캐시 메모리인 L1, L2, L3는 계층을 나누어 존재하는데,

 

레지스터 <- L1 <- L2 <- L3 순으로,

왼쪽이 가장 빠르고 용량이 적고 오른쪽으로 갈수록 용량이 많고 속도가 상대적으로 느리다.

 

 


 

1.6 저장 장치들은 계층구조를 이룬다

 

레지스터는 L0 캐시라고 할 수 있다.

그리고 로컬 디스크(그림의 하드 디스크)는 인터넷 상의 떠돌아 다니는 수 많은 정보 중 내가 다운로드 받은 것이 들어가는 의미에서는

원격 네트워크 서버의 캐시 역할을 수행하고 있는 셈이다.

 


 

1.7 운영체제는 하드웨어를 관리한다.

프로그램이 실행될 때, 작동되는 디스플레이와 같은 하드웨어는 프로그램이 동작시키지 않는다.

이는 프로그램이 작동되고 있는 운영체제가 제공하는 서비스를 활용한다.

 

운영체제는 시스템 내의 하드웨어와 소프트웨어 사이에 운영체제 소프트웨어로서 존재한다. 

운영체제의 주요 두가지 역할은 다음과 같다.

 

1. 통제되지 않은 응용프로그램들이 하드웨어를 잘못 사용하는 것을 방지.

2. 응용프로그램들이 통일된 방식으로 복잡하고 각기 다른 하드웨어를 조작할 수 있도록 함.

 


1.7.1 프로세스

 

프로세스는 실행 중인 프로그램에게만 할당된 운영체제의 추상체이다.

 

(실제로는 운영체제의 부분이 떼져 실행 중인 프로그램에 할당 된 것은 아니기 때문에 추상체라고 표현한 것 같다.)

 

특정 프로그램을 실행하면, 컴퓨터는 그 프로그램을 실행하기 위해 모든 자원을 쓰는 것 처럼 느껴지는데,

이 이유는 다수의 프로세스들이 동일한 시스템에서 동시에 실행되는 것 같기 때문이다.

 

(동시에 실행된다는 것은 곧, 여러 프로세스의 인스트럭션(명령)들이 한데 섞여 수행된다는 말이다.)

 

하지만 사실 매우 짧은 순간에 주요 프로세스들의 실행 권한을 실시간으로 바꿔가며 수행하고 있다.

 

이를 문맥 전환이라고 하는데, 이 방식을 사용하여 운영체제는 프로세스들을 교차로 수행한다.

 

프로세스의 상태를 '컨텍스트' 라고 하는데 현재 수행 상태를 말한다.

 

 

만약 특정 프로세스가 수행 상태인 컨텍스트인데, 갑자기 새로운 프로세스의 수행을 명령받았다고 해보자.

(간단히 유저로부터 Enter 키를 입력받았다고 생각하자.) 

1. 새로운 프로세스의 실행 명령을 전달 받은 응용프로그램은 시스템 콜이라는 함수로 운영체제에게 제어권을     전달한다.

2. 운영체제는 현재 수행중인 프로세스로부터 새로운 프로세스로 제어를 옮기기전에 현재 실행중이던 프로세        스의 컨텍스트를 저장한다.

3-1. 그 후 새 프로세스의 컨텍스트를 복원하고 제어권을 실행할 프로세스에게 넘겨주고 해당 프로세스는 실행      된다. (이 과정이 문맥 전환이다.)

3-2. 이때 이 프로세스는 이전에 중단되었던 구간부터 다시 수행된 후 수행을 마친다.

4. 운영체제는 마친 프로세스로부터 제어권을 되돌려 받고, 이전에 중단된 프로세스의 컨텍스트를 복구하며          제어권을 다시 전달한다.

 

이 과정 속에서 운영체제가 뭔가 지시하고 전달하는 그 역할은 운영체제의 '커널'이 수행한다.

 

커널은 운영체제 코드의 일부분으로 메모리에 상주하는데,

응용프로그램이 어떤 작업을 요청하면 컴퓨터는 시스템 콜을 실행해서 커널에 제어를 넘겨준다.

그러면 제어를 넘겨받은 커널은 요청된 작업을 수행하여 응용프로그램으로 리턴해준다.

 

커널은 별도의 프로세스가 아닌, 모든 프로세스들을 관리하기 위한 '시스템의 코드와 자료구조의 집합'이다.

1.7.2 쓰레드(Thread)

 

프로세스는 쓰레드라고 하는 다수의 실행 유닛으로 구성되어있다.

각 쓰레드는 프로세스 내의 컨텍스트에서 실행되며 동일한 코드와 전역 데이터를 공유한다.

 

쓰레드는 프로세스보다 데이터의 공유가 더 쉽고, 효율적이어서 중요성이 더욱 커지고 있다.

 

다중 쓰레딩도 다중 프로세서를 활용할 수 있다면 프로그램의 실행 속도를 빠르게 하는 방법이 될 수 있다.

 


 

1.7.3 가상 메모리

 

프로세스는 메인 메모리에 해당 프로세스 만의 메모리 주소를 할당 받아 사용하는데, 이를 추상화하여 가상 메모리라고 한다.(프로세스 만의 메모리 공간이 할당 되는 것)

 

가상 메모리(왼쪽)와 메인 메모리(오른쪽)

그림을 참고하여 가상 메모리를 살펴보자.

그림의 왼쪽은 프로세스가 할당 받은 가상의 메모리이다.

 

Data는 프로그램 코드와 데이터를 의미한다. 코드는 모든 프로세스들이 같은 고정 주소에서 시작하고, 다음에 C 전역 변수에 대응되는 데이터 위치들이 따라온다.

 

다음으로는 런타임 힙이 있다.

힙은 C 표준함수인 malloc이나 free를 호출하며 동적으로 크기가 변환된다.

 

그 위로는 공유 라이브러리(math, stdio 등)가 존재하는데 동적 링크를 학습할 때 더 자세히 공부해 보자.

 

스택은 가상 메모리의 사용자 부분의 맨 위에 존재하는데 힙과 마찬가지로 동적으로 변환된다.

함수를 호출할 때마다 스택이 커지고, 리턴될 때마다 줄어든다.

이 스택은 컴파일러가 사용하는 공간이고, 더 자세한 것은 3장에서 공부할 예정이다.

 

전체 가상 메모리 공간(사용자 윗부분)의 꼭대기에는 커널 가상 메모리 공간으로, 커널을 위해 예약되어있다.

이영역은 응용프로그램이 제어하거나 확인할 수 없고, 커널 코드 내의 함수를 호출할 수도 없다.

이러한 작업은 직접 커널에게 간접적인 요청으로 수행된다.

 

 

가상 메모리가 작동하기 위한 기본적인 아이디어는

프로세스의 가상 메모리의 내용을 디스크에 저장하고, 메인 메모리를 디스크의 캐시로 사용하는 것이다.

 


 

1.7.4 파일

 

모든 입출력 장치는 파일로 모델링(추상화)한다.

시스템의 모든 입출력은 유닉스I/O라는 시스템 콜들을 이용하여 파일을 읽고 쓰는 형태로 이루어진다.

 

파일로 모델링 된 덕분에 입출력 장치들은 응용프로그램 안의 서로 다른 시스템에서도 통일된 방식으로 입출력을 동작할 수 있게 된다.

 


1.8 시스템은 네트워크를 사용하여 다른 시스템과 통신한다.

네트워크는 또 다른 입출력으로 볼 수 있다.

마치 무선 메모리 저장소(원격 저장소)인 것이다.

 

네트워크 통신을 통해 사용자는 메인 메모리의 데이터를 네트워크 어댑터로 복사하여 다른 컴퓨터가 받을 수 있도록 한다. 그리고 반대로, 다른 사용자의 데이터를 원격 서버로부터 받아 내 로컬 디스크에 저장할 수 있다.

 


1.9 중요한 주제들

1.9.1 Amdahl(암달)의 법칙

 

특정 시스템의 성능 개선의 효율성을 체크할 때, 암달이 만든 이 공식을 사용할 수 있다.

주요 아이디어는 우리가 어떤 시스템의 한 부분의 성능을 개선할 때, 전체 시스템에 대해서 그 부분이 얼마나 많은 가중치를 가지는가, 와 이 부분이 얼마나 성능이 향상되었는가 와 관계가 있다는 것이다.

암달의 법칙을 살펴보면 특정 percent가 낮은(즉 전체 시스템 내의 가중치가 낮은) 기능의 속도를 무한대로 끌어올린다 하더라도, 전체 시스템의 성능 변화는 크지 않다는 것을 알 수 있다.

 

이를 통해 전체 시스템을 상당히 빠르게 하기 위해서는 전체 시스템의 가중치가 높은 부분을 개선시키는 것이 중요하다는 것을 알 수 있다.

 


1.9.2 동시성과 병렬성

 

시대를 거듭하면서 지속적으로 컴퓨터의 성능개선을 위해 노력했다. 성능 개선이란 더 많은 일을 , 더 빨리 처리함을 의미한다. 

 

동시성이란 다수의 동시에 벌어지는 일을 처리하는 시스템에 용어를 사용하며,

병렬성이란 동시성을 사용해서 시스템을 더 빨리 동작하게 하는 것을 말할 때 사용한다.

 

<쓰레드 수준 동시성>

 

하나의 운영체제 커널의 제어 안에 여러 개의 프로세서를 가지는 경우를 멀티 프로세서 시스템이라고 한다.

현재는 하나의 칩 안에 여러개의 프로세서가 들어있는 경우가 많다고 한다.

이런 방식을 통해서 더 많은 양을 처리할 수 있게 하려고 한다.

 

멀티쓰레딩이라고도 하는 하이퍼쓰레딩은 하나의 프로세서가 여러 개의 제어 흐름을 실행할 수 있게 해주는 기술이다.

이는 CPU 내의 하드웨어인 PC와 Register를 여러개 보유하며 가능해진다.

 

멀티쓰레딩과 멀티 프로세서 시스템같은 동시에 여러 작업을 수행하게 하는 CPU 칩의 향상을 잘 활용하기 위해 응용프로그램의 작성 방법을 찾고자 노력하고 있다고 한다.

 

<인스트럭션 수준 병렬성>

 

최근의 프로세서들은 여러 개의 인스트럭션을 한번에 실행할 수 있게 되었는데, 이는 여러가지 교묘한 기법을 이용한다.

 

교묘한 기법 중 하나인 파이프라인은 첫번째 인스트럭션의 2번째 task를 실행할 때, 두번째 인스트럭션의 1번째 task를 실행하는 방식이고, 이는 동시에 여러개의 인스트럭션 과정을 수행할 수 있게 된다.

 

사이클 당 한 개 이상의 인스트럭션을 실행할 수 있는 프로세서를 슈퍼 스케일러라고 한다.

 

<싱글 인스트럭션, 다중 데이터 병렬성(SIMD)>

 

최신 프로세서는 SIMD 병렬성 모드를 수행할 수 있는,

즉, 한개의 인스트럭션이 병렬로 다수의 연산을 수행할 수 있는 특수 하드웨어를 가지고 있다.

 

대개 영상, 소리, 동영상 데이터 처리를 위한 응용프로그램의 속도를 개선하기 위해 제공된다.

 

 


 

DP (Dynamic Programing : 동적 계획법)

DP란 문제에 대한 답을 찾기위해 완전탐색을 하되, '체계적이고 효율적'으로 탐색하는 풀이법이다.

 

단계는 다음과 같다.

1. 크고 복잡한 문제를 작은 문제들로 나눈다.

2. 하위 문제의 답을 계산한다. 이때, 중복 계산해야 하는 하위 문제는 재계산하지 않는다.

3. 하위 문제에 대한 답을 통해 원래 문제에 대한 답을 계산한다.

 

3번을 최적 부분 구조(Optimal Substructure)라고 하는데, 이는 하위 부분 문제에서 구한 최적의 답이 합쳐져서 최종 문제의 답을 구할 수 있는 구조를 말한다.

 

예를 들면, 피보나치 수열의 n번째 수를 구하기 위해서 우리는 지금까지 재귀를 사용해 왔다.

하지만 재귀를 사용하게 되면 불필요하게 fibo(n-1) 나 그 이하의 값들을 여러번 계산해야 한다.

이 방법은 완전탐색이고 성능이 느려지는 요인이다.

 

이를 해결하기 위해, for문을 통해 fibo(1), fibo(0) 이 둘의 베이스케이스를 이용해서 밑에서 위로 값을 구해 배열에 저장하면 fibo(n)을 구하기 위해 그 이하의 값들을 여러번 계산할 필요가 없어진다.

 

이러한 접근법을 동적 계획법 이라고 한다.