인터프리터
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
인터프리터는 소스 코드를 직접 실행하거나 중간 코드로 변환하여 실행하는 컴퓨터 프로그램으로, 컴퓨터 과학 초창기부터 존재해왔다. 인터프리터는 소스 코드를 처리하는 방식에 따라 바이트코드 인터프리터, 스레드 코드 인터프리터, 추상 구문 트리 인터프리터, JIT 컴파일러, 자체 해석기, 템플릿 인터프리터 등으로 나뉜다. 컴파일러와 비교하여 개발 속도가 빠르고 이식성이 높지만, 실행 속도가 느리고 보안에 취약할 수 있다는 단점이 있다. 액션스크립트, 자바스크립트, 파이썬 등 다양한 프로그래밍 언어에서 사용되며, 명령 줄 언어, 글루 언어, 가상화, 샌드박스 환경 등 다양한 분야에 응용된다.
더 읽어볼만한 페이지
- 프로그래밍 언어 구현 - 어셈블리어
어셈블리어는 사람이 이해하기 쉬운 니모닉 기호로 기계어 명령을 표현하는 저수준 프로그래밍 언어로서, 각 프로세서마다 사양이 다른 어셈블리어가 존재하며 하드웨어 직접 제어, 성능 최적화, 저수준 시스템 프로그래밍 등에 활용된다. - 프로그래밍 언어 구현 - 컴파일러
컴파일러는 고급 프로그래밍 언어로 작성된 소스 코드를 컴퓨터가 이해할 수 있는 저급 언어로 변환하는 프로그램으로, 어휘 분석, 구문 분석, 의미 분석, 최적화, 코드 생성 등의 단계를 거쳐 목적 코드를 생성하며, 네이티브 컴파일러, 크로스 컴파일러 등으로 분류되어 다양한 분야에서 활용된다.
인터프리터 | |
---|---|
기본 정보 | |
유형 | 프로그래밍 언어 도구 |
목적 | 소스 코드를 한 줄씩 읽고 실행 |
특징 | 디버깅 용이 빠른 개발 가능 플랫폼 독립성 |
작동 방식 | |
구문 분석 | 소스 코드를 읽고 의미 분석 |
실행 | 분석된 코드를 즉시 실행 |
오류 처리 | 오류 발생 시 즉시 중단 또는 예외 처리 |
장점 및 단점 | |
장점 | 개발 속도 향상 플랫폼 독립성 디버깅 용이성 |
단점 | 실행 속도 느림 메모리 사용량 높음 |
사용 분야 | |
웹 개발 | JavaScript, PHP, Python |
스크립트 언어 | Bash, Perl |
교육용 언어 | Python, Scratch |
예시 | |
Python | print("Hello, world!") |
JavaScript | console.log("Hello, world!"); |
컴파일러와의 비교 | |
컴파일러 | 소스 코드를 기계어로 번역하여 실행 파일 생성 |
인터프리터 | 소스 코드를 한 줄씩 해석하고 즉시 실행 |
추가 정보 | |
동적 디스패치 | 동적 디스패치를 사용하여 런타임에 메서드 호출 결정 |
2. 역사
인터프리터 기법은 "해당 하드웨어가 직접 해석하지 않는 프로그램"을 받아 "프로그램으로 구현된 추상적인, 또는 가상 컴퓨터로 해석 실행한다"는 프로그램 실행법으로, 컴퓨터 등장 초기 혹은 그 이전부터 존재했다.
인터프리터는 실행할 수 있는 명령어 집합과 실행 순서대로 나열된 명령어 목록으로 구성된다.[3] 각 명령어는 변경하려는 데이터와 변경 방법에 대한 정보를 포함한다.[3] 예를 들어, `ADD Books, 5`는 `Books` 변수에 5를 더하라는 요청으로 해석될 수 있다.[3]
만능 튜링 머신은 "어떤 튜링 머신에 대해서도, 그것을 모의할 수 있는 튜링 머신"으로, 일종의 에뮬레이터 또는 인터프리터이며, 전자식 컴퓨터가 탄생하기 이전에 고찰되었다.
EDSAC (실용적인 기능을 가진 프로그램 내장 방식의 세계 최초 전자 계산기로 여겨짐)에서도 이미 일종의 인터프리터가 구현되었음이 기록에 남아있다. EDSAC의 프로그래밍 기법이 쓰여진 ''The Preparation of Programs for an Electronic Digital Computer'' 2장 ''§ 2-22 Interpretive subroutines'' 에서는 복소수 연산 등의 서브루틴을 명시적으로 호출하는 것이 아니라, 통상의 가감산 등과 같은 형식의 프로그램을 인터프리터로 해석하여 해당 서브루틴을 이용하는 방식을 설명한다. 또한 일본에서도 펀치 카드를 입력으로 패치 패널의 배선에 의한 프로그래밍으로 처리하는 기계에서, 배선에 의해 일종의 인터프리터와 같은 것을 구현하여, 펀치 카드의 내용을 데이터가 아닌 프로그램처럼 취급하는 사례가 있었다고 한다.[28]
최초의 인터프리트 방식 고급 언어는 리스프였다. 리스프는 1958년 스티브 러셀이 IBM 704 컴퓨터에 최초로 구현하였다. 러셀은 존 매카시의 논문을 읽고 리스프의 eval 함수가 기계어로 구현될 수 있다는 것을 발견했는데, 이는 매카시를 놀라게 했다.[40] 그 결과 리스프 프로그램을 실행, 더 정확히 말해 "리스프 식의 평가"에 사용될 수 있는 작동하는 리스프 인터프리터가 만들어졌다.
최초의 Lisp 인터프리터는 스티브 러셀이 IBM 704 상에 구현했다. 존 매카시가 "Lisp의 논문"[29]에서 "수학적"으로 제시한 것이었는데, 매카시 자신은 구현할 수 있다고 생각하지 않았다. 논문을 읽은 대학원생이었던 러셀이 구현 가능하다고 말하며 수학적인 기술로부터 변환하여 기계어로 구현해 보였다.[30][31]
편집 인터프리터의 개발은 대화형 컴퓨팅의 필요성에 의해 영향을 받았다. 1960년대 시분할 시스템의 도입으로 여러 사용자가 동시에 컴퓨터에 접근할 수 있게 되었고, 편집 인터프리터는 코드를 실시간으로 관리하고 수정하는 데 필수적인 요소가 되었다. 최초의 편집 인터프리터는 메인프레임 컴퓨터를 위해 개발되었을 가능성이 높으며, 여기서 프로그램을 즉석에서 생성하고 수정하는 데 사용되었다. 초기 편집 인터프리터 중 하나는 PDP-1 컴퓨터용으로 1960년대 후반에 개발된 EDT(TECO용 편집기 및 디버거) 시스템이다. EDT를 통해 사용자는 명령과 매크로를 조합하여 프로그램을 편집하고 디버깅할 수 있었으며, 이는 현대 텍스트 편집기 및 대화형 개발 환경의 길을 열었다.
1960년대에는 (현재의 Java 등과 마찬가지로) 프로그래밍 언어를 중간 표현으로 컴파일하고, 그것을 인터프리터로 실행하는 방식도 일반화되었다 (p 코드 머신 참조).
3. 종류
인터프리터는 다양한 작업을 위한 광범위한 명령어를 가지지만, 기본적인 수학 연산, 분기, 메모리 관리에 대한 명령어는 대부분의 인터프리터를 튜링 완전하게 만든다.[3] 많은 인터프리터는 가비지 수집기 및 디버거와 통합되어 있다.[3]
인터프리터는 소스 코드를 처리하는 방식에 따라 여러 종류로 나뉜다.
3. 1. 바이트코드 인터프리터
Emacs Lisp는 Lisp 소스를 고도로 압축하고 최적화한 표현인 바이트코드로 컴파일되지만, 기계어는 아니므로 특정 하드웨어에 묶여 있지 않다. 이 "컴파일된" 코드는 C로 작성된 바이트코드 인터프리터에 의해 해석된다. 이러한 컴파일 인터프리터를 ''컴프리터''라고도 부른다.[10][11] 바이트코드 인터프리터에서 각 명령어는 1바이트로 시작하므로 최대 256개의 명령어를 가질 수 있지만, 모두 사용되지는 않을 수 있다. 일부 바이트코드는 여러 바이트를 차지할 수 있으며 임의로 복잡할 수 있다.
소스 코드를 실행 가능한 형태로 만들기 위해, 소스 코드를 구문 트리로 변환하고, 더 나아가 중간 코드(바이트 코드 등)와 같은 중간 표현으로 변환한 후 실행하기도 한다. 중간 코드를 바이트 코드라고 부르는 처리계에서 해당 인터프리터를 바이트 코드 인터프리터라고 부른다. Java|자바영어나 .NET Framework|.NET 프레임워크영어처럼, 중간 코드의 사양을 공개하고 파일에 기록하는 것도 있고, 사양을 공개하지 않고 처리계 내부에서만 사용하는 것도 있다.
Emacs Lisp는 바이트 코드로 컴파일되며, LISP|Lisp영어의 소스를 고도로 압축하고 최적화한 표현으로 만들지만, 이는 기계어 코드가 아니다. 이 "컴파일"된 코드를 해석하는 것이 바이트 코드 인터프리터이다(그 자체는 C로 작성되어 있다). 이 경우 컴파일된 코드는 가상 머신의 기계어 코드이며, 가상 머신은 하드웨어로 구현되지 않고 바이트 코드 인터프리터로 구현되어 있다.
3. 2. 스레드 코드 인터프리터
스레드 코드 인터프리터는 바이트코드 인터프리터와 유사하지만, 바이트 대신 포인터를 사용한다. 각 "명령어"는 함수 또는 명령어 시퀀스를 가리키는 단어이며, 선택적으로 매개변수가 뒤따른다. 스레드 코드 인터프리터는 명령어를 가져와 가리키는 함수를 호출하는 과정을 반복하거나, 첫 번째 명령어를 가져와서 해당 명령어로 점프하며, 모든 명령어 시퀀스는 다음 명령어를 가져와 점프하는 것으로 끝난다. 바이트 코드와 달리 사용 가능한 메모리 및 주소 공간 외에는 서로 다른 명령어 수에 실질적인 제한이 없다. 스레드 코드의 전형적인 예는 Forth 코드로, Open Firmware 시스템에서 사용된다.[34]
3. 3. 추상 구문 트리 인터프리터
해석과 컴파일 사이의 또 다른 접근 방식은 소스 코드를 최적화된 추상 구문 트리(AST)로 변환한 다음, 이 트리 구조를 따라 프로그램을 실행하거나 이를 사용하여 네이티브 코드를 JIT(Just-in-time) 컴파일하는 것이다.[12] 이 방식에서는 각 문장을 한 번만 파싱하면 된다. 바이트코드에 비해 AST는 전역 프로그램 구조와 문장 간의 관계를 유지하며(바이트코드 표현에서는 손실됨), 압축 시 더욱 간결한 표현을 제공한다는 장점이 있다.[13] 따라서 AST는 JIT 컴파일러를 위한 더 나은 중간 형식으로 바이트코드보다 더 제안되었다. 또한 런타임 중에 시스템이 더 나은 분석을 수행할 수 있다.
그러나 인터프리터의 경우 AST는 구문과 관련된 노드가 유용한 작업을 수행하지 않고, 순차적이지 않은 표현(더 많은 포인터 순회를 요구함)과 트리를 방문하는 오버헤드로 인해 바이트코드 인터프리터보다 더 많은 오버헤드를 발생시킨다.[14]
3. 4. JIT(Just-In-Time) 컴파일
실행 시점에 중간 표현을 기본 기계어로 컴파일하는 JIT(Just-in-time) 컴파일은 인터프리터, 바이트코드 인터프리터, 컴파일 간의 경계를 모호하게 만든다.[12] 이는 바이트코드 또는 AST가 처음 컴파일될 때 시작 시간과 메모리 사용량 증가를 감수하고 기본 코드 실행의 효율성을 제공한다. 최초로 공개된 JIT 컴파일러는 일반적으로 1960년 존 매카시가 LISP에 대해 수행한 작업으로 여겨진다.[15] 적응적 최적화는 인터프리터가 실행 중인 프로그램을 프로파일링하고 가장 자주 실행되는 부분을 기본 코드로 컴파일하는 보완적인 기술이다. 이 기술은 1980년대 스몰토크와 같은 언어에서 나타났다.[15]
JIT 컴파일은 최근 Java, .NET Framework, 대부분의 최신 JavaScript 구현 등에서 사용되면서 언어 구현자들 사이에서 많은 관심을 받고 있다.
3. 5. 자체 해석기
자기 인터프리터는 자기 자신을 해석할 수 있는 프로그래밍 언어로 작성된 프로그래밍 언어 인터프리터이다. BASIC으로 작성된 BASIC 인터프리터가 그 예이다. 자기 인터프리터는 자기 호스팅 컴파일러와 관련이 있다.[20]
Lisp 및 프롤로그와 같은 일부 언어는 우아한 자기 인터프리터를 가지고 있다. 자기 인터프리터(특히 반사적 인터프리터)에 대한 많은 연구가 Lisp의 방언인 Scheme 프로그래밍 언어에서 수행되었다. 일반적으로 튜링 완전 언어는 자체 인터프리터 작성을 허용한다. Lisp는 Lisp 프로그램이 기호 및 기타 목록의 목록이기 때문에 그러한 언어이다. XSLT는 XSLT 프로그램이 XML로 작성되기 때문에 그러한 언어이다. 메타프로그래밍의 하위 도메인은 DSL을 작성하는 것이다.[21]
3. 6. 템플릿 인터프리터
템플릿 인터프리터는 컴파일러와 인터프리터의 구분을 모호하게 만드는 특별한 인터프리터 설계 방식이다. 템플릿 인터프리터는 가능한 모든 바이트코드(또는 효율적인 중간 표현)를 호스트 하드웨어에서 실행될 수 있는 네이티브 기계 명령어에 직접 매핑되는 대규모 배열 형태로 키-값 쌍으로 유지한다.[16][17] 이를 "템플릿"이라고 한다. 특정 코드 세그먼트가 실행될 때, 인터프리터는 템플릿에서 해당 opcode 매핑을 로드하거나 해당 위치로 점프하여 하드웨어에서 직접 실행한다.[18][19]
이러한 설계는 템플릿 인터프리터를 전통적인 인터프리터보다는 JIT(Just-In-Time) 컴파일러와 매우 유사하게 만들지만, 전체 코드 세그먼트에서 CPU 실행 가능 명령의 최적화된 시퀀스를 생성하는 대신 언어의 코드를 한 번에 한 opcode씩 네이티브 호출로 변환하기 때문에 기술적으로 JIT는 아니다. 인터프리터는 호출을 직접 구현하는 대신 하드웨어로 직접 전달하는 단순한 설계를 사용하기 때문에, 다른 유형의 인터프리터보다 훨씬 빠르며, 어느 정도 버그가 발생할 가능성도 적지만, 인터프리터가 플랫폼 독립적인 가상 머신/스택 대신 여러 다른 아키텍처에 대한 변환을 지원해야 하므로 유지 관리가 더 어렵다는 단점이 있다.
현재까지 널리 알려진 언어의 템플릿 인터프리터 구현은 Java 공식 레퍼런스 구현인 Sun HotSpot JVM[16] 내의 인터프리터와 Google V8 자바스크립트 실행 엔진의 Ignition 인터프리터뿐이다.
4. 작동 방식
인터프리터는 실행 가능한 명령어 집합과 프로그래머가 실행하려는 순서대로 나열된 명령어 목록으로 구성된다. 각 명령어는 프로그래머가 변경하려는 데이터와 데이터를 변경하는 방법에 대한 정보를 포함한다. 예를 들어, 인터프리터는 `ADD Books, 5`를 읽고 이를 `Books` 변수에 5를 더하라는 요청으로 해석할 수 있다.
인터프리터는 다양한 작업을 수행하도록 특화된 광범위한 명령어를 가지고 있지만, 기본적인 수학 연산, 분기, 메모리 관리에 대한 명령어들을 일반적으로 찾을 수 있으며, 이는 대부분의 인터프리터를 튜링 완전하게 만든다. 많은 인터프리터는 가비지 수집기 및 디버거와 긴밀하게 통합되어 있다.
인터프리터는 각 가상 명령에 연결된 명령 본체와 디스패치 기구를 가지며, 호스트 머신에서 실행 가능하다. 가상 명령군으로 구성된 코드를 인수로 인터프리터가 실행되면 가상 명령에 따라 제어가 이동하고 해당 명령 본체가 실행된다. 이를 반복함으로써 인터프리터는 코드를 실행한다.
인터프리터 방식은 컴파일러 방식보다 실행 속도가 느린 경향이 있는데, 컴파일러는 프로그램 내 문 해석을 실행 전에 한 번만 수행하지만, 인터프리터는 문마다, 실행 시마다 반복하기 때문이다.
4. 1. 명령 본체
'''명령 본체'''(instruction body)는 가상 명령을 호스트 언어로 에뮬레이트하는 코드이다.[25] 명령 본체가 호스트 머신에서 실행됨으로써 가상 명령이 사실상 실행된다. 즉, 명령 본체는 가상 명령의 구현이다. 예를 들어 이항 합 가상 명령ADD2
에 대응하여 C 언어로 작성된 push(pop() + pop())
는 명령 본체이다.4. 2. 디스패치
디스패치(dispatch)는 가상 명령에 기반하여 명령 본체 간의 제어 흐름을 전환하는 메커니즘이다.[25][26] 가장 단순한 예시는 Switch 문(Jump 명령)을 사용하는 것이다. 특정 가상 명령 실행 후, 다음 가상 명령에 해당하는 명령 본체로 제어를 이동(Jump)하여 해당 가상 명령의 구현을 실행한다.인터프리터 방식은 컴파일러 방식보다 실행 속도가 느린 경향이 있다. 컴파일러는 프로그램 내 문 해석을 실행 전에 한 번만 수행하지만, 단순한 인터프리터 구현에서는 문마다, 실행 시마다 반복하므로 실행 성능이 떨어진다. 단순한 인터프리터 구현에서는 변수 접근 시 식별자와 메모리 위치 간 매핑을 확인해야 하며, 이를 실행 중 여러 번 반복해야 하므로 속도가 느려진다.
또한 디스패치는 인터프리터가 본질적으로 갖는 비용이다. 컴파일 방식에서는 컴파일 시점에 디스패치에 해당하는 명령 본체 정렬이 이루어지므로 실행 시 디스패치 비용이 발생하지 않는다. 따라서 인터프리터 방식에서는 (디스패치 비용 × 명령 수)만큼의 추가 비용이 본질적으로 발생한다.
디스패치는 분기 예측을 어렵게 한다. 따라서 파이프라인 방식을 사용하는 CPU에서 속도에 영향을 미친다.[32] 영향의 크기는 분기 예측기의 성능에 좌우되며, 2000년대 이전 CPU에서는 인터프리터의 속도 저하가 이 페널티로 인해 발생한다고 여겨졌다. 예측기 성능이 향상된 2010년대 이후 CPU (예: x86 Haswell)에서는 그 영향이 작다.[33]
5. 컴파일러와의 비교
고급 프로그래밍 언어로 작성된 프로그램은 인터프리터에 의해 직접 실행되거나, CPU가 실행할 수 있도록 컴파일러(및 어셈블러와 링커)에 의해 기계어로 변환된다. 컴파일러는 소스 코드에서 기계어 수준으로의 거의 모든 변환을 한 번에 수행하는 반면, 인터프리터는 문장이나 함수가 실행될 때마다 이 변환 작업의 일부를 수행한다.[5] 효율적인 인터프리터에서는 많은 변환 작업이 분리(factor out)되어 프로그램, 모듈, 함수 또는 문장이 처음 실행될 때만 수행되므로, 컴파일러의 작동 방식과 매우 유사하다.
컴파일러 시스템은 링커를 포함하여 독립 실행형 기계어 프로그램을 생성하는 반면, 인터프리터 시스템은 고급 프로그램에서 설명하는 작업을 수행한다.[5]
전통적인 컴파일에서 링커의 실행 가능한 출력은 일반 운영 체제에서 실행될 때 재배치 가능하며, 오브젝트 코드 모듈과 유사하지만 이 재배치가 실행을 위해 프로그램을 로드할 때 동적으로(런타임에) 수행된다는 차이점이 있다.
프로그래밍 언어 처리계의 구현에는 인터프리터와 컴파일러 두 가지가 있다고 여겨져 왔다. 인터프리터는 실행을 수행하지만, 컴파일러는 실행을 수행하지 않는다는 차이가 있다.
일반적으로 인터프리터의 장점과 단점은 다음과 같이 설명된다.
- 장점: 프로그램을 작성하는 도중에도 일단 작성된 부분까지 실행할 수 있으며, 프로그래머가 기대한 대로 동작하는지, 혹은 기대와 다르게 동작하는지 조기에 확인하고 발견할 수 있다. 수정 후 빠르게 실행하여 재확인할 수 있다.
- 단점: 실행 속도가 느리다. 루프 부분 등에서는 한 번 구문 해석한 문장도 매번 새롭게 처음부터 해석과 실행을 하므로 컴파일러 방식에 비해 실행 속도가 느리다.
이후, 이러한 단점을 해소하기 위해 1990년대 무렵에는 매번 고급 언어에서 기계어로 변환하는 것이 아니라, 중간 언어로 변환함으로써 고속화를 꾀하는 인터프리터 등이 만들어졌다.
6. 장점과 단점
인터프리터 방식은 컴파일러 방식에 비해 실행 속도가 느리다는 단점이 있다. 컴파일러는 프로그램 실행 전에 전체 코드를 한 번에 기계어로 변환하지만, 인터프리터는 코드를 한 줄씩 읽고 실행할 때마다 해석 및 변환 과정을 거치기 때문이다.
단순한 인터프리터의 경우, 반복문(루프)이 실행될 때마다 동일한 문장을 반복해서 해석하고 실행한다. 이는 컴파일러가 실행 전에 전체 코드를 한 번만 변환하는 것과 대조적이다. 변수 접근 시에도 인터프리터는 식별자와 메모리 위치 간의 매핑을 실행 중에 반복해서 확인해야 하므로 속도가 더욱 느려진다.[6]
디스패치(dispatch)는 인터프리터가 가지는 본질적인 비용이다. 컴파일 방식에서는 컴파일 시에 디스패치에 해당하는 명령 본체 정렬이 이루어져 실행 시에는 디스패치 비용이 발생하지 않는다. 반면 인터프리터는 디스패치 비용과 명령 수를 곱한 만큼의 추가 비용이 발생한다.
디스패치는 분기 예측을 어렵게 하여 명령어 파이프라인을 사용하는 CPU의 속도에 영향을 미친다.[32] 2000년대 이전 CPU에서는 인터프리터의 속도 저하가 이 때문이라고 여겨졌으나, 2010년대 이후 CPU(Haswell 등)에서는 예측기 성능 향상으로 영향이 줄었다.[33]
하지만 개발 속도와 실행 속도 사이의 균형을 맞추기 위해 다양한 방법이 개발되었다. 예를 들어, 일부 LISP영어 처리 시스템에서는 인터프리터 코드와 컴파일된 코드를 함께 사용하고 변수를 공유할 수 있다.
컴파일러 방식은 실행 속도가 빠르지만, 개발 과정에서 수정 및 테스트에 시간이 오래 걸린다. 반면, 인터프리터 방식은 실행 속도는 느리지만, 개발 과정에서 수정 및 테스트가 빠르다.
6. 1. 개발 편의성
인터프리터는 소프트웨어 개발 주기 동안 소스 코드 변경 사항을 즉시 실행하여 결과를 확인할 수 있어 개발 속도가 빠르다. 컴파일러는 소스 코드 변경 시 컴파일 및 링킹 과정을 거쳐야 실행 가능하지만, 인터프리터는 이러한 과정 없이 바로 실행할 수 있다. 따라서 프로그램이 클수록 인터프리터가 컴파일러보다 개발 시간을 단축시킨다.[1]REPL(Read-Eval-Print Loop)은 이러한 특성을 활용한 프로그램이다. REPL은 입력을 받아(Read), 입력값을 평가하고(Eval), 평가 결과를 제시하는(Print) 것을 반복하는(Loop) 테스트 환경이다. REPL은 인터프리터뿐만 아니라 컴파일러로도 수행될 수 있지만, 사용자로부터의 입력이 소스 코드의 단편이고 이를 차례대로 실행한다는 점에서 일종의 인터프리터라고 할 수 있다. REPL은 소스 코드를 지정하지 않고 시작한 BASIC과 매우 유사하게 동작하며, 테스트 환경을 겸하고 있어 오류 발생 시 상세한 메시지를 제공한다. 함수형 프로그래밍 언어나 논리 프로그래밍 언어에서는 일반적으로 기계어로 컴파일하는 처리계에서도 REPL을 제공한다.[1]
6. 2. 이식성
인터프리터 방식의 프로그램은 소스 코드로 배포될 수 있다. 각 최종 컴퓨터에서 번역이 필요하며, 이는 더 많은 시간이 소요되지만 프로그램 배포가 컴퓨터 아키텍처에 독립적이 되도록 한다. 그러나 인터프리터 방식의 소스 코드 이식성은 대상 컴퓨터에 적절한 인터프리터가 실제로 있는지에 따라 달라진다. 인터프리터를 소스와 함께 제공해야 하는 경우, 인터프리터 자체가 설치해야 할 부분의 일부이기 때문에 전체 설치 프로세스는 단일 실행 파일의 제공보다 더 복잡하다.한편 인터프리터 방식에서는 소스 코드와 인터프리터가 배포된다. 인터프리터는 기계어 실행 파일이기 때문에 환경에 의존하지만, 소스 코드에는 환경에 의존하지 않는 언어를 채용할 수 있다. 그 경우 환경에 맞춘 인터프리터를 미리 배포해두면, 아키텍처에 의존하지 않는 프로그램의 배포가 가능하다 (이식성이 높다). 또한 인터프리터 대신 JIT 컴파일러를 사용해도 동일한 이점을 얻을 수 있다. 동일 동작의 보증은 인터프리터 구현에 의존하며 (JIT라면 컴파일러), 호환성 버그의 예로 스프레드시트 매크로와 웹 페이지 (HTML)가 있다.
6. 3. 복잡성
AOT 컴파일러 방식에서는 하나의 바이너리를 실행하는 것만으로 프로그램이 기능한다. 반면 인터프리터 방식에서는 먼저 인터프리터를 설치하고 그 위에서 소스 코드를 실행해야 한다. 그 결과 전체 프로그램 크기가 커지고, 사용자의 수고가 늘어나는 경우가 있다. JIT 컴파일 방식에서도 비슷한 복잡성이 발생한다.6. 4. 가독성 및 보안
인터프리터 방식의 코드는 사람이 쉽게 읽고 복사할 수 있어 저작권 측면에서 우려될 수 있다.[1] 그러나 다양한 암호화 및 난독화 시스템이 존재한다.[1] 바이트 코드와 같은 중간 코드의 전달은 난독화와 유사한 효과를 가지지만, 디컴파일러 또는 디스어셈블러를 사용하여 해독될 수 있다.[1]인터프리터용 코드(고급 언어 코드 또는 바이트 코드)는 기계어 바이너리보다 해독이 용이하여(가독성이 높다) 배포 후 디버깅과 수정이 용이하지만, 지적 재산권 보호 문제를 일으킬 수 있다.[2] 이를 위해 암호화 및 난독화 코드를 고려한 언어·시스템이 존재하며, JIT 컴파일 방식에서도 이와 유사한 가독성 관련 특성이 나타난다.[2]
6. 5. 속도
인터프리터 방식은 컴파일러 방식보다 실행 속도가 느린 경향이 있다.컴파일러는 프로그램 내의 문 해석을 실행 전에 한 번만 수행하지만, 단순하게 구현된 인터프리터는 문마다 실행할 때마다 반복하기 때문에 실행 성능이 떨어진다. 단순한 인터프리터에서는 변수에 접근할 때도 식별자와 메모리상의 위치 매핑을 확인해야 하며, 이를 실행 중에 여러 번 반복해야 하므로 속도가 느려진다.[6]
디스패치는 인터프리터가 본질적으로 안고 있는 비용이다. 컴파일 방식에서는 컴파일 시에 디스패치에 상당하는 명령 본체 정렬이 이루어지므로 실행 시에 디스패치 비용이 발생하지 않는다. 따라서 인터프리터 방식에서는 디스패치 비용과 명령 수를 곱한 만큼의 추가 비용이 본질적으로 발생한다.
디스패치는 분기 예측을 어렵게 한다. 따라서 파이프라인 방식을 채택하는 CPU에서 속도에 영향을 미친다.[32] 영향의 크기는 분기 예측기의 성능에 따라 달라지는데, 2000년대 이전의 CPU에서는 인터프리터의 속도 저하가 이 페널티 때문에 발생한다고 여겨졌다. 2010년대 이후 CPU(예: x86 Haswell)에서는 예측기 성능이 향상되어 그 영향은 작다.[33]
인터프리터를 이용한 개발 속도와 컴파일러를 이용한 실행 속도 사이에는 다양한 타협안이 고안되었다. 일부 LISP영어 처리 시스템 등에서는 인터프리터 코드와 컴파일된 코드가 서로 호출할 수 있으며, 변수도 공유할 수 있다. 따라서 특정 루틴을 인터프리터로 평가하고 디버깅한 후, 먼저 컴파일하여 실행 성능을 높이면서 다른 루틴을 계속 개발할 수 있다.
7. 응용 분야
- 인터프리터는 명령 줄 언어 및 글루 언어에 자주 사용된다.
- 자기 수정 코드는 인터프리터에서 쉽게 구현할 수 있다. 이는 LISP영어와 인공지능 연구가 인터프리터의 기원이었던 것과도 관련이 있다.
- 가상 머신을 사용하여, 특정 아키텍처를 다른 아키텍처에서 실행시키는 가상화는 기본적으로 인터프리터이다.
- 샌드박스에서 인터프리터 또는 가상 머신은 소스 코드의 명령을 모두 실제로 실행할 필요가 없다. 특히 컴퓨터 보안을 위협하는 처리는 실행을 거부할 수 있다.
- 더 현대적인 장비에서 구식 및 사용 불가능한 하드웨어를 위해 작성된 컴퓨터 소프트웨어를 실행하기 위한 에뮬레이터로도 활용된다.
8. 프로그래밍 언어
인터프리터 방식으로 실행되는 대표적인 프로그래밍 언어는 다음과 같다.
언어 | 설명 |
---|---|
액션스크립트 | |
APL | |
AWK | |
Bash | |
BASIC | 고전적인 마이크로컴퓨터용 |
Csh | |
Curl | |
dBASE | |
Emacs Lisp | |
FoxPro | |
Io | |
JavaScript | |
MATLAB | |
Perl | |
PostScript | |
Prolog | |
REXX | |
Ruby | |
Python | |
PHP | |
R | |
Scheme | |
Tcl | |
TeX | |
VBScript |
최초의 인터프리트 방식의 고급 언어는 리스프였다. 스티브 러셀은 1958년 IBM 704 컴퓨터에 리스프를 최초로 구현하였다. 러셀은 존 매카시의 논문을 읽고 리스프의 eval 함수가 기계어로 구현될 수 있다는 것을 발견했는데, 이는 매카시를 놀라게 했다.[40] 그 결과 작업 중인 리스프 인터프리터가 만들어졌으며, 이는 리스프 프로그램 실행, 더 정확히 말해 "리스프 식의 평가"에 사용될 수 있었다.
9. 기타
펀치 카드 시스템에서 펀치 카드를 읽어 그 내용을 사람이 읽을 수 있는 문자로 펀치 카드에 인쇄하는 기계를 인터프리터라고 불렀다. 예를 들어 IBM 550|IBM 550영어 (1930년)와 IBM 557|IBM 557영어 (1954년)가 있다.[1]
일반적으로 C 언어는 컴파일러로 처리되지만, 디버깅 및 교육용으로 인터프리터 형식의 C 언어 처리기도 있다. CINT, Ch, C-Terp 등이 그 예이다.[1]
참조
[1]
문서
'In this sense, the CPU is also an interpreter, of machine instructions.'
[2]
문서
Although this scheme (combining strategy 2 and 3) was used to implement certain BASIC interpreters already in the 1970s, such as the efficient BASIC interpreter of the ABC 80, for instance.
[3]
논문
Interpretative sub-routines
1952
[4]
문서
According to what reported by Paul Graham in Hackers & Painters, p. 185, McCarthy said: "Steve Russell said, look, why don't I program this eval..., and I said to him, ho, ho, you're confusing theory with practice, this eval is intended for reading, not for computing. But he went ahead and did it. That is, he compiled the eval in my paper into IBM 704 machine code, fixing Software bug, and then advertised this as a Lisp interpreter, which it certainly was. So at that point Lisp had essentially the form that it has today..."
[5]
웹사이트
Why was the first compiler written before the first interpreter?
https://arstechnica.[...]
2014-11-08
[6]
문서
FOLDOC Interpreter
[7]
웹사이트
Compilers vs. interpreters: explanation and differences
https://www.ionos.co[...]
2022-09-16
[8]
간행물
The Structure and Performance of Interpreters
http://citeseerx.ist[...]
[9]
웹사이트
The Difference Between Compilers and Interpreters
http://www.antlr.org[...]
[10]
서적
Mikroelektronik in der Amateurpraxis
Militärverlag der Deutschen Demokratischen Republik
1987
[11]
논문
Basic-Compreter für U880
1984
[12]
웹사이트
AST intermediate representations
http://lambda-the-ul[...]
[13]
논문
A Tree-Based Alternative to Java Byte-Codes
http://oberon2005.ob[...]
2020-12-20
[14]
웹사이트
Announcing SquirrelFish
http://webkit.org/bl[...]
Webkit.org
2008-06-02
[15]
논문
Efficient implementation of the Smalltalk-80 system
http://portal.acm.or[...]
1984
[16]
웹사이트
openjdk/jdk
https://github.com/o[...]
2021-11-18
[17]
웹사이트
HotSpot Runtime Overview
https://openjdk.java[...]
Openjdk.java.net
2022-08-06
[18]
뉴스
Demystifying the JVM: JVM Variants, Cppinterpreter and TemplateInterpreter
https://metebalci.co[...]
[19]
웹사이트
JVM template interpreter
https://programmerso[...]
[20]
간행물
Logimix: A self-applicable partial evaluator for Prolog
https://web.archive.[...]
1993
[21]
웹사이트
Eigenratios of Self-Interpreters
http://eigenratios.b[...]
2019-11-10
[22]
서적
Encyclopedia of Computer Science and Technology: Volume 28 - Supplement 13
https://books.google[...]
Marcel Dekker, Inc
2016-01-17
[23]
서적
bit 単語帳
共立出版
1990-08-15
[24]
문서
この意味では、CPUは機械語インタプリタであると見ることができる。
[25]
문서
An interpreter dispatches a virtual instruction body to emulate each virtual instruction in turn.
[26]
문서
we defined dispatch as the mechanism used by a high level language virtual machine to transfer control from the code to emulate one virtual instruction to the next.
[27]
문서
現在では、「インタプリタ / コンパイラ」という区分に関しては状況が変わっており、に言わせると『だが、それらは必ずしも相互排他的に2つに分類できるわけではない。なぜなら多くのインタプリタ方式の処理系は、コンパイラが行っているような変換も内部で行っているからだ。』とも言われ、『「インタプリタ言語」あるいは「コンパイラ言語」といった呼称も見掛けることがあるが、これらは単にその言語の規範的実装がインタプリタかコンパイラかを示しているに過ぎない(実際、詳しく調べれば、実験的な程度の実装まで含めれば両方ともあるということも多い)。』という見解も出てくることになる。高水準言語は基本的に抽象であり、(理想的には)特定の実装からは独立している。しかし、動的プログラミング言語のようにインタプリタでの実装が向いている方向性の言語、あるいはその逆もあるということは確かである.
[28]
웹사이트
日本のソフトウェアの草創期:座談会:日本のソフトウェアの草創期
http://id.nii.ac.jp/[...]
[29]
문서
Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I のこと
[30]
문서
History of Lisp の、§3 の最後のほうに、次のようにある「S.R. Russell noticed that eval could serve as an interpreter for LISP, promptly hand coded it, and we now had a programming language with an interpreter. (段落) The unexpected appearance of an interpreter ...(後略)」
[31]
문서
ポール・グレアムの『ハッカーと画家』(原著「Hackers & Painters、185ページ)によれば、マッカーシーは「ラッセルは『ねえ、この eval
をプログラムしようか』と言った。…私は『ほう、ほう。君は理論と実際を混同している。この eval は読み物として書いたもので、実際に動かすために書いたものじゃない』と答えた。しかし彼はそれをやってのけた。つまり彼は私の論文にある eval を IBM 704 の機械語にコンパイルして、バグを修正し、それを LISP インタプリタだと宣伝したし、実際それはそのとおりだった。だからその時点で LISP は今日のような形態を本質的に備えていた」と述べたという。
[32]
논문
Branch Prediction and the Performance of Interpreters - Don’t Trust Folklore
None
2015-02
[33]
논문
Branch Prediction and the Performance of Interpreters - Don’t Trust Folklore
None
2015-02
[34]
문서
つまり、近年では高速化にはキャッシュのほうが重要なので、高速化に有利か否かはわからない。
[35]
웹사이트
AST intermediate representations
http://lambda-the-ul[...]
Lambda the Ultimate forum
[36]
논문
A Tree-Based Alternative to Java Byte-Codes
http://www.ics.uci.e[...]
[37]
웹사이트
Annoucing SquirelFish
http://webkit.org/bl[...]
[38]
간행물
Efficient implementation of the Smalltalk-80 system
http://portal.acm.or[...]
1984
[39]
문서
[40]
서적
Hackers & Painters
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com