프로그램 언어를 해석하고 실행시키는 대표적인 방법으로 Compile 과 Interpret 방식이 있다.

Compile 작업은 Compiler 에 의해 실행되고, Interpret 작업은 Interpreter 에 의해 실행되는데, 두 컨셉이 명확하게 다르기 때문에 

많은 프로그래밍 언어들은 둘 중 한가지 방식을 통해 언어를 실행하도록 설계된다. (Java 와 같이 두가지를 모두 채용하는 경우도 있다!)

그렇기 때문에 Compiler 와 Interpreter 를 이해하는 것은 어떤 언어를 배우던지간에 해당 언어의 구동원리를 배울 수 있는 중요한 선행학습이라할 수 있겠다.


컴파일 (Compile)

프로그래밍 언어를 Runtime 이전에 기계어로 해석하는 작업 방식이다.
이때 원래의 소스를 원시 코드, 바뀐 코드를 목적 코드(Object Code) 라 한다.

런타임 이전에 Assembly 언어로 변환하기 때문에 구동 시간이 오래걸리지만, 구동된 이후는 하나의 패키지로 매우 빠르게 작동하게 된다.
구동시에 코드와 함께 시스템으로부터 메모리를 할당받으며 할당받은 메모리를 사용하게 된다.

런타임 이전에 이미 해석을 마치고 대게 컴파일 결과물이 바로 기계어로 전환되기 때문에 OS 및 빌드 환경에 종속적이다.
그러므로 OS 환경에 맞게 호환되는 라이브러리와 빌드환경을 구분해서 구축해줘야 한다.

Compile 언어의 대표격으로 C / C++ 와 같은 언어들을 들 수 있으며, Java 역시 Byte Code 로 바꾸기 위한 과정에서 컴파일을 수행한다.


인터프릿 (Interpret)

런타임 이전에 기계어로 프로그래밍 언어를 변환하는 컴파일 방식과 다르게, 런타임 이후에 Row 단위로 해석(Interpret) 하며 프로그램을 구동시키는 방식이다.

프로그래밍 언어를 기계어로 바로 바꾸지않고 중간 단계를 거친 뒤, 런타임에 즉시 해석하기 때문에 바로 컴팩트한 패키지 형태로 Binary 파일을 뽑아낼 수 있는 Compile 방식에 비해 낮은 퍼포먼스를 보이게 된다.

런타임에 직접 코드를 구동시키는 특징이 있기 때문에 실제 실행시간은 느리며, 대신 런타임에 실시간 Debugging 및 코드 수정이 가능하다.

또한 메모리를 별도로 할당받아 수행되지 않으며, 필요할 때 할당하여 사용한다. 이와 관련되어 코드의 흐름 자체도 실제 필요할 때, 실제 수행되어야하는 시점에 수행되기 때문에 덕타이핑(Duck Typing) 이 가능한 측면이 있으나, 반대로 정적 분석이 되지않는 Trade off 를 갖고 있다.

 

대표적인 Interpreter 언어로는 Javascript 와 같은 스크립팅 언어들이 있다. 하지만, 스크립트 언어 뿐 아니라 컴파일 이후의 동작에서 Interpret 을 수행하는 언어들도 많이 존재한다.


많은 프로그래밍 언어들의 인터프리터는 해석을 위한 Virtual Machine 을 두고, Machine 위에서 Interpret 을 수행하게 되는데, 이 때 해석의 기반이 되는 머신들이 OS 환경들을 지원해줌으로써, 해당 방식으로 인터프리터는 OS 및 플랫폼 에 종속되지않는 프로그램 구동이 가능하게 된다.
(이런 특징을 지닌 Interpreter 는 Java 의 JVM 과 Python 의 Analyzer 가 있겠다.)


컴파일러와 인터프리터의 차이는 잘 이해하고 언어와 환경을 파악하는데 활용하는 것이 중요하다.

 

면접에서 단골처럼 등장하는 질문이자, 컴퓨터 공학과 시험에서 한번쯤은 보았을 법한 CS 기본 지식을 정리하고자 한다.

 

컴퓨터는 데이터를 저장할 수 있는 몇가지 종류의 공간들을 갖고 있고, 해당 공간들은 쓰임새가 다르고 만들어진 이유가 다르기 때문에 각각 I/O 작업에 있어서 다른 퍼포먼스를 낸다.

 

그 중에서도 Access 에 대한 다음 Computing Operation 의 속도 비교는 알아두어야 한다.

 

 - CPU Register

 - Context Switch

 - Memory Access (RAM)

 - Disk Seek (HDD)

 

위의 Operation 들에 대한 속도 비교 결과는 빠른 순서대로 다음과 같다.

 

1. CPU Register Access

2. Memory Access

3. Context Switching

4. Disk Seek

 

(1) CPU 레지스터에 대한 접근은 단 한번의 CPU 사이클만으로 이루어지기 때문에 즉각적으로 이루어진다.

한 사이클이라는 것은 말그대로 번개와 같은 속도로 이루어진다는 뜻이다.

 

(2) Memory Access 는 일반적으로 RAM 에서 데이터를 읽어내는 것을 말하며, 당연히 RAM 의 목적에 맞게 HDD 로부터 읽어오는 것보다 빠르다.

일반적인 상태에서의 작업은 레지스트리에 접근하는 것에 비견될만큼 빠를 수 있지만 논리 구조 위에서 동작하기 때문에 Virtual Memory Swapping 과 같은 작업에서 자유로울 수 없으며 이런 경우에는 Disk Access 만큼 느려질 수도 있다. 

 

(3) Context Switching 는 대체적으로 빠른 접근이 보장이 된다. 하지만 여러개의 프로세스가 동시에 실행되며 스위칭이 빈번하게 이뤄질 경우 굉장히 느려질 수도 있다.

 

(4) Disk Seek. HDD 에 대한 Disk Seek 은 위에 언급한 Operation 들에 비해 빠를 수가 없는 작업이지만 캐싱을 통해 비약적인 성능 향상이 가능하다.

BUS 에서의 병목을 피할 수 있으며 캐싱을 통해 Main Memory 에 Access 하는 것 만큼의 퍼포먼스를 기대할 수도 있다.

 

 

면접에서 갑작스레 질문받은 내용이라 당황했던 적이 있었다.

알고있었던 내용이라 답변은 잘 했으나... 끝나고나서 다시 점검해볼만큼 기본기가 아직 충분치 못한 것 같아 정리해둔다.

 

 




캐시란 데이터를 임시로 저장해두는 장소를 말한다. 임시로 저장하여 사용하는 데이터의 종류는 제한이 없기 때문에 작게는 메모리에서 크게는 Logic 혹은 그 이상을 저장할 수 있다.


Cache 의 목적은 로직을 처리하는 데 있어서 빠른 접근성을 제공하는 것이며, 단순히 수동적으로 보관하는 것에 그치지 않고 이를 응용해서 작업의 결과를 저장함으로써 해당 로직의 불필요한 수행을 줄여주는 능동적인 역할까지 수행한다.


캐시가 될 수 있는 것은 일반적으로 로컬 메모리부터 별도의 디스크 볼륨까지 다양하지만 Cache 로 사용하기 위한 가장 중요한 요건은 데이터로의 접근성이다.


Cache 에 대한 접근성은 어떤 경우에도 로직 상에서 원하는 데이터에 직접 접근하거나 만들어내는 비용보다 저렴해야 한다. 그래야만 캐시로서의 의의가 있는 것이다.


그렇기 때문에 Cache 는 접근성이 빠른 공간(Space)에 빠른 자료구조를 사용한다.


캐시서버로 이용되는 서버들은 I/O 에 최적화된 공간이 사용되며 당연히 이에 대한 접근성에 있어서 효율적인 REST 등의 방법을 사용한다. 


컴퓨터 내에서 사용되는 캐시는 RAM보다 빠른 L1,L2 레지스터를 캐시로 사용하고, 프로그램 내에서 구현된 Software Cache 라면 접근에 용이한 Map 과 같은 자료구조에 인메모리(Inmemory)로 저장한다.


다음은 캐시를 이해하는 데 중요한 용어들이다.


 - origin : origin 혹은 origin server 는 캐시에 저장할 실 데이터가 존재하는 공간이다. 웹 캐시라면 DB 서버일 수도 있고, SW 내에서라면 파일 혹은 실행 함수 그 자체일 수도 있다.


 - cache expire : 프로세스 내에서 사용하는 인메모리 캐시나 영구히 상주해야하는 정보를 가진 캐시가 아니라면 Cache 는 Expire Date 를 갖고 있으며 해당 시간이 지나면 상한(Stale) 상태가 된다.


 - cache freshness : 캐시가 만료되지 않은 경우를 fresh 한 캐시라 하고 만료된 경우 stale cache 라 한다.


 - cache hit : 참조하려는 데이터가 캐시에 존재할 때 해당 캐시를 조회하는 걸 Cache hit 이라 한다.


 - cache miss : 참조하려는 데이터가 캐시에 존재 하지 않는 경우


 - cache hit ratio : 적중률로 전체 참조 횟수 대비 Cache hit 된 비율을 의미한다. 실질적으로 캐시의 설계는 Cache hit Ratio 를 높이는 데 초점을 둔다.



다음은 캐시의 동작에 대한 정책들이다.


 Cache Read : 


 - Cache-aside 방식 : 데이터를 참조 하기 전에 참조하고자 하는 값이 캐시에 존재하는지 확인한다. 여기서 값을 직접 비교하기 보다는 키를 이용해서 캐시에 접근한다. 

 Cache에 존재한다면 Cache에서 데이터를 가져온다. 만약 Cache에 존재하지 않는다면 origin에서 데이터를 가져오고 이를 캐시에 저장한다.


 - RT/WT/Write back 방식 : 캐시를 Main Data Source 로 사용하기 때문에 캐시에서만 데이터를 조회한다.

 RT/WT 방식(Read Through / Write Through) 은 Read Scalability 가 가장 뛰어나다.



 Cache Write :


 - Cache-aside 방식 : 캐시를 Application Level 에서 직접 갱신시켜준다. 개발자가 Flow 를 이해하고 Update / Evict 시켜줘야 하며, 그렇지 않으면 Cache 데이터와 DB 데이터가 불일치하는 Stale 현상이 발생한다.


 - Read Through / Write Through 방식 : 데이터의 쓰기시 캐시와 실제 저장공간의 데이터 둘다 최신화 시키는 작업이다. 

 캐시를 메인 Database 로 사용하는게 특징적이며, 캐시에 데이터를 먼저 업데이트하고 캐시에서 Main Database 를 즉시 갱신시킨다.

 양쪽의 데이터를 동일하게 유지할 수 있지만, 쓰기 시에 추가 부하가 생긴다는 단점이 있다.


 - Write Back 방식 : 데이터의 쓰기시 캐시의 데이터만 최신화 하고 해당 Cache 를 Evict 시켜놓는다. 이 후 RT/WT 방식처럼 캐시 값을 마킹된 기준으로 origin 으로 직접 반영(Write) 하는데, 캐시가 별도의 큐를 이용해서 Database Source를 비동기로 Update 시켜준다.

 Write Performance 와 DB Scalability 에 있어서 가장 뛰어나다.

 쓰기 작업이 Cache 에서만 발생하지만, Cache 가 만료되는 시점까지 Origin 에 Write Failure 가 발생한다면 데이터를 영구 손실할 위험이 존재한다. 



 Cache Replacement


  웹 캐시의 경우 자동 expire 하거나 명시적으로 cache 를 지워주는 동작을 해주지만, 캐시를 Scheduling 에 사용하는 컴퓨터나 알고리즘의 경우 Replicement Policy 를 갖는다.



 다음은 몇가지 대표적인 알고리즘들이다.


  - FIFO(First In First Out) : 오래된 캐시를 먼저 비우고 새로운 캐시를 추가하는 방식이다.


  - LIFO(Last In First Out) : 가장 최근에 반영된 캐시가 먼저 지워진다.


  - LRU(Least Recently Used) : 가장 최근에 사용되지 않는 순서대로 캐시를 교체한다. 가장 오랫동안 사용되지 않은 캐시가 삭제되며 일반적으로 사용되는 방식이다.


  - MRU(Most Recently Used) : 가장 최근에 많이 사용되는 순서대로 캐시를 교체한다. 휘발성 메모리를 이용해야 하는 특수한 상황에 사용된다.


  - Random : 말그대로 랜덤으로 캐시를 교체한다.


 운영체제를 배웠다면 페이지 교체 알고리즘이 Cache Replacement 정책을 사용한다는 것을 알 수 있을 것이다.



본 포스팅에서는 넓은 범위의 Cache 의 정의와 목적, 정책들에 대해 정리해보았다.


이론적인 부분이고 웹 캐시와는 조금 다르기도 하지만 중요한 기본 개념은 잘 숙지해두자.


참조 : 

https://en.wikipedia.org/wiki/Cache_replacement_policies

https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/

https://gomguard.tistory.com/115

https://onecellboy.tistory.com/260

https://dzone.com/articles/using-read-through-amp-write-through-in-distribute



컴퓨터는 CPU에서 tick 을 발생시키며 1970년 1월 1일부터 발생된 tick 의 수를 계산해서 시간을 측정한다.


이 시작점을 UNIX 계열에서는 POSIX time 혹은 Epoch time 이라 하며, 1970년 1월 1일 00:00:00 UTC 이고, 


이때부터 경과 시간을 초로 환산해서 사용한다.


이런 방식에는 몇가지 문제가 있는데, 먼저 윤초(예를 들어 1998년 12월 31일 23:59:60)는 표현할 수 없이 무시된다.


현재 32비트 운영체제에서 초 시간을 지정하는 time_t 자료형은 32비트 Integer 이기 때문에, 


2038년이 되면 overflow 문제가 발생한다.


이를 2038 Years Problem 이라 하며 2038년 1월 19일 03:14:07 UTC 가 되면 오버플로우가 발생해서 


32비트 유닉스 시스템의 시간은 음수가 되어버린다.


그렇기 때문에 현재 int32 를 int64 로 바꾸는 노력을 계속하고 있고, 지속적으로 수정이 진행되고 있다.





OSI 7 계층은 컴퓨터 네트워크를 이해하는 데 있어서 빼놓을 수 없는 개념이다.

면접에서 기본기 확인 용도로 많이 물어보기도 하는 개념으로 너무나 기본적으로 알고 있어야 하는 내용이다.


OSI 7계층이란 인터넷을 위한 네트워크 연결 표준으로 응용, 표현, 세션, 전송, 네트워크, 데이터링크, 물리 계층의 7계층으로 나뉘어져있다. 데이터를 전송할때마다 각각의 층에서 인식할 수 있는 헤더를 붙이게 되는데 이를 캡슐화라고 한다.


컴퓨터에서 소켓을 통해 인터넷으로 데이터를 전송할 때, 컴퓨터들 사이에서 이를 이해할 수 있는 공통의 포맷을 만들어두었다고 이해하면 쉽다. 다음 그림을 참고하자.


왼쪽과 오른쪽에는 Pack 과 Unpack 이라는 두개의 화살표가 있다. OSI 의 각 계층을 어떤식으로 통과하는지를 이해하기 쉽게 표현한 그림이다.

이해를 돕자면, 메시지를 네트워크로 전송할 때에는 왼쪽의 화살표 방향대로 OSI 의 각 계층을 따라 내려가면서 헤더를 추가한다.


그리고, 네트워크로부터 메시지를 전송받을 시에는 OSI의 아랫쪽 계층부터 위로 올라오면서 차례대로 Header 를 분석해서 최종적인 Message 를 받는다.

추가 설명 이전에 각 계층에 대해 먼저 알아보자.


- 7계층은 응용 계층(Application Layer)으로 HTTP, DNS, FTP 등의 프로토콜이 사용되며 사용자가 네트워크에 접근할 수 있도록 해주는 계층이다. 서비스를 제공한다.


- 6계층은 표현 계층(Presentation Layer)으로 데이터를 정해진 표현 형태로 변환한다. 확장자나 인코딩 등이 포함된다.


- 5계층은 세션 계층(Session Layer)으로 포트 연결이라고도 할 수 있다. 상호간의 세션이 유효한지 확인하고 설정하며, SSH, TLS 등의 프로토콜이 포함된다. 포트 번호를 기반으로 통신 세션을 구성한다.


- 4계층은 전송 계층(Transport Layer)으로 엔드포인트 간 제어와 에러를 관리한다. 여기서 붙은 헤더를 세그먼트(TCP의 경우, UDP의 경우 Datagram이라 한다.)라 하며 TCP, UDP 등의 프로토콜이 포함된다. 세그먼트에 Port 정보가 포함된다. 이 계층의 장비로는 게이트웨이가 있다.


- 3계층은 네트워크 계층(Network Layer)으로 이곳에서 붙는 헤더를 패킷이라 한다. 노드 대 노드 간 연결을 위해 존재하며 대표적 프로토콜은 IP이다. 이 계층의 장비로는 라우터가 있다.


- 2계층은 데이터 링크 계층(Data Link Layer)으로 이곳에서 붙는 헤더를 프레임이라 한다. MAC 어드레스간 주소 접근을 담당한다. 이 계층의 장비에는 브릿지, 스위치가 있다.


- 1계층은 물리 계층(Physical Layer)으로 bit 흐름을 전송하기 위한 기능을 조정하며 이더넷 프로토콜이 여기에 해당된다. 이 계층의 장비는 허브 또는 리피터라 한다.



 주목해서 보면 좋은 계층은 2~4 계층이다. 이 부분이 디바이스로 직접 연결을 담당하는 계층들이다.

IP, MAC Address, Port 정보가 모두 포함되기 때문에 방대한 인터넷 연결에 있어서 실제 상대방의 주소를 찾기 위한 헤더 정보는 전부 이 계층에 포함된다. 

바꿔말하면 들어온 패킷의 해당 정보들이 디바이스와 맞지 않다면 해당 계층에서 걸러진다. 좀 더 응용해보자면, 통신을 중계하기 위해서는 해당 계층들의 역할을 수행할 수 있는 장비가 필요하다.


그 위의 계층은 잘 알려진 TCP/SSH/HTTP 등의 프로토콜을 기술하고 맨 윗 계층에서 사용자는 실제 Application 을 통해 메시지를 받을 수 있다.



+ Recent posts