tech.junha.kr

컴파일, 인터프리터, JIT

컴파일

컴파일러: 다른 프로그래밍언어로 바꾸어주는 번역 프로그램 or 고수준 언어를 기계어로 번역하는 프로그램을 말한다.

대표적으로 C언어 계열 언어가 컴파일러를 사용한다.

초기엔 프로그램을 작성하기 위해서는 컴퓨터 위에서 바로 돌아가는 기계어를 통하여 프로그래밍을 했다. 그러나 이런 과정은 생산성, 기기 간 호환성, 디버깅등 모든 면에서 효율적이지 않다. 따라서 컴퓨터공학이 발전하면서 많은 부분 추상화된 고수준 언어를 작성하고 이를 번역기를 통해 기계어로 번역하기 시작했는데, 이 번역기가 바로 컴파일러이다.

(폰노이만이 제자들에게 잔머리 굴린다고 나무라 친 것이 현재의 컴파일러이다.)

컴파일러는 다음과 같은 세 분류로 나뉜다.

  • 원시 코드를 바로 기계어로 변환하는 정적 컴파일(Static Compilation)

  • 바이트 등의 중간 코드를 기계어로 변환하는 AOT 컴파일(Ahead-Of-Time Compilation)

  • 실행시 최초 한 번에 한해 컴파일을 거치는 JIT 컴파일(Just-In-Time Compilation)

컴파일을 하는 대신, 소스를 한꺼번에 번역하지 않고 명령 하나하나를 실행할 때마다 해석하여 계산하는 방법도 있는데, 이 해석기를 인터프리터라고 한다.

인터프리터

컴파일러가 번역기라면 인터프리터는 통역기이다.

웹 브라우저에서 동작하는 자바스크립트, 데이터베이스 언어인 SQL, 자체 프로그래밍 언어 중 Python, Ruby등이 인터프리터를 사용한다.

장점

1. 수정이 간단하다.

컴파일 언어의 단점은 수정이 용이하지 않아, 수정 사항이 발생하면 다시 컴파일을 해야 되는데, 작은 프로그램일 경우에는 문제가 되지 않지만 컴파일이 몇 시간씩 걸리는 덩치 큰 프로그램에서는 문제가 된다. 하지만 인터프리터는 속도는 느리지만는 수정이 간단하다.

컴파일러는 소스 코드를 번역해서 실행 파일을 만들기 때문에 프로그램에 수정 사항이 발생하면 소스 코드를 다시 컴파일해야 한다. 프로그램이 작고 간단하면 문제가 없지만 프로그램 덩치가 커지면 컴파일이 시간 단위가 되는 일이 많아진다. 하지만 인터프리터는 소스 코드를 수정해서 실행시키면 끝. 이 장점 때문에 인터프리터 언어는 수정이 빈번히 발생하는 용도의 프로그래밍에서 많이 사용된다. 이 장점을 최대한 살려서 인터프리터를 적극적으로 채용한 것이 스크립트 언어다.

디버그가 수월하다. 구문 오류의 경우 예전 인터프리터의 경우에는 정말로 한줄씩 읽어서 실행했기에 구문 오류가 나오는 부분 전까지는 멀쩡하게 실행되는 경우가 많았다. 그러다보니 실행되지 않는 부분에는 오류가 있어도 오류로 처리되지 않는 경우도 존재하였다. 하지만 최근에는 성능 등의 이유로 파일을 실행하면 파일 전체를 컴파일 하는 방식을 사용하기 때문에 처음부터 구문 오류를 전부 잡아주는 인터프리터도 많아지고 있다.

2. 설계가 쉽다.

고급 프로그래밍 언어 중에 대부분이 인터프리터 언어다. 인터프리터는 자체적으로 실행기능이 없기 때문에, 목적 프로그램(.exe)을 만드는 곳만 컴파일러 언어를 사용하고 대부분 코드를 인터프리터 언어로 짜는 식으로 결합하는 경우가 많다. 인터프리터 언어가 많아지는 이유는 프로그래밍 언어의 설계가 쉽기 때문이다.

프로그래밍 언어 차원에서 동적인 기능을 지원하기 유리하다. 어떤 모듈을 사용할지 결정하는 것이나 사용자가 입력한 임의의 프로그램을 평가하는 것이라던가 심지어 이미 실행되고 있는 코드 자체를 수정하며 실행하는 등의 기능을 구현할 수가 있다.

단점

1. 소스 코드가 쉽게 공개된다.

컴파일되는 언어도 기계어를 읽으면 프로그램이 어떻게 동작하는지 알 수 있으며, 디컴파일러를 이용하여 원본 언어로 변환하는 것도 불가능은 아니다. 하지만 바이트코드 등으로 컴파일해서 배포하는 경우도 잘 없을 뿐더러, 컴파일 전/후 언어가 비교적 단순한 편이라서 쉽게 디컴파일이 가능하다. 암호화를 해도 어차피 실행될 때는 복호화되니 무용지물이다. 그나마 식별자들을 전부 이상하게 바꾸고 해석하기 힘들게 난독화하는 정도 외에 할 수 있는 방법이 없다.

2. 문자를 읽는 특성상, 문자를 입력하는 함수에서 취약점이 발견될 가능성이 높다.

그런데 이런 소스코드의 쉬운 유출 가능성이 오히려 인터프리터 계열 언어들의 강점이 되기도 한다. 온갖 불법 복제나 해킹의 표적이되기 쉽고, 편리한 접근성 때문에 많은 사용자들이 몰려, 전 세계적인 연구개발 성과 공유가 의도치 않게 가능하다.

인터프리터는 실행 시마다 소스 코드를 한 줄씩 기계어로 번역하는 방식이기 때문에 실행 속도는 정적 컴파일 언어보다 느리다. 이를 해결하기 위하여 바이트코드 컴파일러를 이용해 소스 코드를 가상머신 타겟의 바이트코드로 변환하거나, 반복적으로 쓰이는 함수와 클래스 등의 기계어 코드를 캐싱하는 JIT 컴파일러를 인터프리터에 내장하는 방식이 도입되었다.

JIT

프로그램을 실행하는 시점에서 필요한 부분을 즉석으로 컴파일하는 방식을 말한다. 동적번역이라고도 한다.

C나 C++에서 하는 것처럼 프로그램을 실행하기 전에 처음 한 번 컴파일하는 것과 대조적이다.
보통 인터프리터방식의 언어 구현들이 성능 향상을 목적으로 도입하는 경우가 많다.

JIT 컴파일러는 같은 코드를 매번 해석하는 대신 처음 실행될 때 인터프리트를 하면서 자주 쓰이는 코드를 캐싱한 뒤, 이후에는 캐싱된 코드를 가져다 쓰기 때문에 인터프리터의 느린 실행 속도를 개선할 수 있다. 따라서 사실 JIT '컴파일러'보다는 JIT '인터프리터'가 더 정확한 표현이다. 바이트코드 컴파일을 사용하는 Java도 바이트코드를 기계어로 번역할 때 JIT 컴파일러를 사용한다.

단점

1. 정적 컴파일된 프로그램에 비해 실행 속도 면에서 손해를 본다.

초기 구동 시에는 소스 코드(혹은 바이트코드)를 실행 단계에서 컴파일하는 데에 시간과 메모리를 소모하기 때문이다. 특히 실행 시간이 매우 짧은 경우에는 애써 컴파일된 코드를 제대로 울궈먹기도 전에 프로그램이 끝나는 배보다 배꼽이 더 큰 상황이 벌어지기도 한다.
크게 나눠서 HotSpot VM과 같이 메소드(함수) 단위로 JIT 컴파일을 하는 방식과, 그보다 더 작은 단위에서 프로그램 실행 흐름을 실시간으로 추적하며 컴파일할 코드를 탐색하는 Tracing JIT 방식으로 분류할 수 있다. 특히 Tracing JIT의 경우에는 실행 시점에만 알 수 있는 정보를 컴파일에 적극적으로 반영하기 때문에 이론적으로는 정적 컴파일 방식보다 컴파일 속도가 더 빨라질 수도 있다.

2. JIT 컴파일러는 잠재적으로 상당한 보안 문제를 가지고 있다.

미리 컴파일된 코드를 실행하는 것이 아닌, 런타임에 동적으로 코드를 생성하여 실행한다는 특징 때문에특히 JIT 컴파일러 자체에 버그가 있는 경우 곧바로 보안취약점이 되는 경우가 많다. 대표적으로 인텔 CPU게이트로 유명한 스펙터 보안취약점은 JIT에 의존하는 JavaScript 엔진을 가진 브라우저에서만 발생했다. 오라클의 HotSpot VM 또한 JIT 컴파일러 버그로 인한 다수의 보안취약점이 있었다.
참고로 iOS에서는 상기한 보안문제를 이유로 네트워크로 바이너리나 코드를 다운받아 실행하는 것을 금지하고 있다. 구글 크롬이나 파이어폭스의 iOS 버전이 자체 엔진을 쓰지 못하고 애플이 제공하는 WebKit의 웹뷰를 사용할 수밖에 없는 주요 이유 중 하나다.[5] 요즘 브라우저들은 JavaScript 엔진으로 JIT를 쓰기 때문이다. iOS도 편법으로 구현이 가능하나 정책이 이를 허용하지 않아 스토어에 올릴때 reject된다.

출처:나무위키