모던 자바스크립트 딥다이브를 읽다가 중간에 모르는 내용이 있을 때 마다
책 제목처럼 딥~다이브 학기 위해 위키백과와 MDN 사이트, 챗 지피티를 통해 공부하고 있다.
예전에 코딩 테스트를 준비 할 때
파이썬은 인터프리터 언어라서 실행속도가 느리고 C 나 C++ 이 컴파일러 언어이기 때문에
실행 속도가 빠르다는 내용을 봤었는데
인터프리터 언어가 뭔지, 컴파일러 언어가 뭔지 모르고 그냥 지나갔었다.
이번 기회에 찾아봤다.
초기 컴퓨터 프로그램은 어셈블리어로 작성 되었다.
그러나 서로 다른 CPU 아키텍처가 등장 할 때 마다 매번 똑같은 프로그램을 서로 다른 어셈블리어로 작성하는 비용이 커지면서 고급 프로그래밍 언어의 필요성이 두각되었다.
어셈블리어
기계어와 일대일 대응이 되는 컴퓨터 프로그래밍의 저급 언어이다.
기계어
기계어(機械語)는 CPU가 직접 해독하고 실행할 수 있는 비트 단위로 쓰인 컴퓨터 언어를 통틀어 일컫는다. 기계어는 프로그램을 나타내는 가장 낮은 단계의 개념이다.
고급 프로그래밍 언어란 사람이 이해하기 쉽고 추상화된 특징을 가진 언어를 가리킨다.
추상화된 특징을 가진 고급 프로그래밍 언어를 기계가 이해할 수 있는 저급 언어로 번역 시키는 것이 컴파일러
이며, 컴파일러
를 사용하는 대표적인 언어는 c
, c++
이다.
컴파일러는 고급 언어를 저급 언어로 옮김의 과정에서 반드시 지켜야 하는 두 가지 조건이 존재한다.
입력받은 프로그램의 의미를 충실히 따라 저급 언어로 옮겨야 한다.
고급 언어를 저급 언어로 옮겼을 때 성능이 실행 속도가 빨리진다든지의 장점이 있어야 한다. 그렇지 않다면 컴파일을 수행 할 이유가 없어진다.
위 두가지 조건을 만족하는 컴파일러로 인해 인간이 이해할 수 있는 고급 언어인 c , c++
을 이용해 소스 코드를 실행시키면 , 기계가 이해 할 수 있도록 컴파일 한 실행 파일
이 생성되고
실행 파일을 실행시켜 소스코드를 실행한다.
기계어로 옮겨서 실행하였기 때문에 속도 면에서도 매우 빠르지만, 실행 파일을 생성해야 한다는 점에서 저장 공간을 차지한다는 단점이 존재하기도 한다.
해당 유튜브를 들어가보면 실행 방식의 차이를 직관적으로 볼 수 있다.
성능이 우수
이전에 말했던 것 처럼 컴파일 과정에서 최적화가 되며, 기계어로 직접 변환되기 때문에 하드웨어에 최적화되어 효율적인 실행이 가능하다.
컴파일 시간 오류 검출
소스 코드에 오류가 있을 경우 실행 전 컴파일 하는 과정에서 오류가 검출된다. 소스코드를 실행하는 런타임에 발생할 수 있는 일부 오류를 미리 방지하는데 도움이 된다.
코드의 숨김
컴파일 된 코드는 기계어로 변환되어 소스 코드의 로직이 직접 노출되지 않아 소스 코드를 공개하지 않아도 실행 파일을 배포 할 수 있다.
컴파일 시간이 길다
큰 프로젝트의 경우 컴파일 하는 시간이 상당히 길어질 수 있다. 만약 컴파일 하는데 10분이 걸리는 프로젝트에서 한 줄만 변경하고 실행하려 해도 다시 컴파일을 10분간 기다리고 실행해야 하는ㄷ 단점이 존재한다.
메모리 사용량이 크다
실행 파일이 메모리를 차지한다.
제한된 이식성
특정 컴파일러나 플랫폼에 종속된 코드가 있다면, 이를 다른 환경으로 이식하는데 어려움을 겪을 수 있다. 예를 들어 intel cpu 에 맞춰 컴파일 된 실행 파일이 있다면 해당 실행 파일은 intel cpu 가 이해 할 수 있는 기계어로 번역된 실행파일이다.
해당 실행파일은 다른 제조사인 AMD cpu 는 실행하지 못할 수 있기 때문에 제조사별 컴파일 파일을 만들어야 한다.
컴파일 전 고급 언어
#include <iostream>
int main() {
for (int i = 0; i < 5; ++i) {
std::cout << i << std::endl;
}
return 0;
}
컴파일 후 저급 언어
main:
mov DWORD PTR [rsp-4], 0 ; int i = 0;
jmp .L2
.L3:
mov eax, DWORD PTR [rsp-4] ; std::cout << i;
mov esi, eax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov edi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
call std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
add DWORD PTR [rsp-4], 1 ; ++i
.L2:
cmp DWORD PTR [rsp-4], 4 ; i < 5;
jle .L3
xor eax, eax ; return 0;
ret
인터프리터는 프로그래밍 언어의 소스코드 (고급언어) 를 바로 실행하는 컴퓨터 프로그램 또는 환경을 말한다.
인터 프리터 언어는 컴파일러 언어의 단점(컴파일 시간이 길다 , 제한된 이식성
)을 보완하기 만든 언어이다.
하지만 성능을 조금은 포기한 ..
인터프리터 언어가 소스코드를 바로 실행하기 위해서는 중간 언어
, 가상 머신
두 가지 개념이 존재한다.
고급 언어인 소스코드를 가상 머신이 이해 할 수 있도록 인터프리터 언어는 중간 언어로 옮긴다.
여기서 중간 언어란 고급 언어로 작성된 소스 코드를 번역하고, 해당 플랫폼에서 실행 가능한 형태로 변환하는 중간 단계의 언어 코드이다.
저급 언어에 비해서 비교적 추상화된 형태를 가지며, 여러 플랫폼에서 실행 가능하도록 설계되어 있다.
인터프리테이션 되기 전 고급 언어
# 반복문 예제
for i in range(5):
print(i)
인터프리테이션 된 후의 중간 언어
# CPython 인터프리터가 생성하는 일부분 (간소화된 형태)
# LOAD_CONST: 상수를 스택에 로드
# FOR_ITER: 반복문의 다음 요소를 가져오고 반복 조건을 확인
# STORE_FAST: 로컬 변수에 값을 저장
0 LOAD_NAME 0 (range)
2 LOAD_CONST 0 (5)
4 CALL_FUNCTION 1
6 GET_ITER
8 FOR_ITER 12 (to 22)
10 STORE_FAST 0 (i)
12 LOAD_FAST 0 (i)
14 PRINT_ITEM
16 PRINT_NEWLINE
18 JUMP_ABSOLUTE 8
어셈블리어에 비해서 비교적 이해하기 쉽고, 추상화 된 형태로 존재한다.
컴파일러 언어는 직접 어셈블리어로 번역하여 CPU 에서 실행하였다면
인터프리터 언어는 가상 머신이 이해 할 수 있는 중간 언어로 번역한 후 가상 머신에서 실행한다.
중간 언어는 바이트 언어
라고 불리기도 한다.
가상 머신이란 중간 언어를 실행하기 위한 소프트웨어나 하드웨어 기반의 가상적 실행 환경을 의미한다.
예를 들어 내가 script.py
라는 고급 언어로 작성 되어 있는 고급 언어를 실행시키고 싶다면
터미널에서
python script.py
파이썬의 인터프리터 (가상머신) 를 이용해서 고급언어를 중간 언어로 번역 시키고 , 가상 머신에서 실행시킨다.
이렇게 고급언어를 중간 언어로 번역하여 가상 머신을 이용해서 실행시키는 것은 장점과 단점을 갖는다.
플랫폼 독립성
어떤 환경이든 상관 없이 소스코드를 실행 할 수 있는 가상 머신만 설치되어 있다면 해당 소스코드를 실행 시킬 수 있다. 이는 플랫폼 간 이식성이 뛰어나다.
빠른 개발 속도
소스 코드를 번역하는 컴파일 시간이 존재하지 않기 때문에 빠른 개발 속도를 제공 할 수 있다.
인터 프리터 언어는 중간 언어로 번역되는 것이 런타임 (실행) 도중에 번역이 된다.
컴파일러 언어는 소스 코드 전체를 번역 후 실행 시키는 것과 다르게
소스 코드를 흐름에 맞게 읽어가며 중간 언어로 번역 -> 번역 된 중간 언어를 메모리에 적재 -> 실행
의 흐름으로 진행된다.
이 과정을 JIT (Just in time) compile
이라고 한다.
실행 도중에 중간 언어로 번역되니까
이를 통해 실행되던 도중 모메리 관리나 적재 된 변수들을 확인하며 디버깅이 용이하다는 장점이 존재한다.
인터프리터 언어는 실행 시에 코드를 해석함으로 컴파일 언어에 비해 일반적으로 실행 속도가 늘리 수 있다.
컴파일 언어는 전체 소스 코드를 번역해둔 실행파일이 있다면 바로 실행하면 되지만
인터프리터 언어는 실행 할 때 마다 번역하고 실행해야 하기 때문에 컴파일 언어에 비해 실행이 느리다.
인터프리터 언어는 소스 코드를 배포하면 가상 머신만 존재한다면 어디에서나 실행 가능한 높은 이식성
이 존재하면서도 트레이드 오프로 인해
소스코드가 노출 될 수 있기 때문에 보완적인 측면에서 취약 할 수 있다.
코드를 한줄씩 실행하며 동적으로 해석하므로 전체적인 실행 성능이 떨어질 수 있다.
JIT
컴파일컴파일러 언어와 인터프리터 언어의 장단점을 보완한 컴파일 방식으로 자바스크립트는 JIT 컴파일을 사용한다.
고급 언어를 런타임 중 가상 머신이 이해 할 수 있는 중간 언어 형태로 변환 (인터프리터 언어의 장점
)하고 다시 중간 언어를 기계어로 컴파일(컴파일러 언어의 장점
)하여 실행하는 방식이다.
또한 런타임 중 프로그램의 동작을 모니터링 하고 , 코드를 동적으로 최적화 하는 컴파일러의 장점도 취한다.
반복적으로 실행되는 루프나 코드 블록을 인식하고 인터프리터에 비해 빠르게 실행 할 수 있다.
또 컴파일러 언어는 소스코드 전체를 컴파일러 언어로 변경한 후 실행하여 많은 메모리를 소비한다는 단점이 존재하였지만 JIT 컴파일
은 프로그램이 실행 중일 때 필요한 부분만 컴파일 하므로 메모리를 효율적으로 사용 할 수 있다.
출처
https://ko.wikipedia.org/wiki/%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0
https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC
https://www.youtube.com/watch?v=nrxtQcCLHo4&t=8s