9.7 Compiling on the Fly (온 더 플라이 컴파일링)

이전 절에서 프로그램을 쓰는 프로그램에 대해서 알아보았다. 각각의 예에서 생성된 프로그램은 소스 형태였으며, 실행을 위해서는 컴파일하거나 인터프리트되어야 한다. 그러나 소스 보다는 머신 인스트럭션을 생성함으로써 즉시 실행할 준비가 된 코드를 생성하는 것이 가능해진다. 이것은 "온 더 플라이(on the fly)" 또는 "저스트 인 타임(just in time)" 컴파일링이라는 용어로 불리우며, 첫 번째 용어가 좀 더 오래된 용어이고, 요즘은 JIT라는 약자로도 쓰이는 두 번째 용어가 더 많이 쓰이고 있다. 그리고 컴파일된 코드가 부득이하게 이식성이 가질 수 없긴 하지만, 반면에 매우 빠르게 실행될 수 있다는 장점을 가지게 된다. 다음 표현식을 보자.

        max(b, c/2)

위의 계산은 반드시 c를 둘로 나누고, 그 결과를 b와 비교하여 큰 값을 선택해야 한다. 만약에 이 장의 앞에서 보았던 가상 머신을 사용하여 표현식을 평가한다면 divop에서 0으로 나누는지에 대한 검사를 제거할 수도 있다(Anakin : 여기선 생략했지만 앞에 나오는 코드 중에 0으로 나누는지 검사하는 부분이 있습니다.). 왜냐하면, 2는 절대 0이 될 수 없기에 검사는 무의미하다. 하지만 가상 머신을 구현하기 위한 어떠한 설계도 검사를 제가할 수 있는 방법은 없다. 즉, 모든 나눗셈 연산의 구현은 제수가 0인지를 비교하여야 한다는 것이다.

이것은 코드를 어디서 동적으로 생성해야 할 것인가에 대한 도움을 줄 수 있다. 만약에 표현식을 위한 코드를 단지 미리 정의된 연산들을 연장하여 사용하는 것이 아니라 직접 생성하여 사용한다면 0이 아닌 제수에 대해서는 검사를 하지 않아도 된다. 사실 더 나아가 모든 표현식이 max(3*3, 4/2)와 같은 상수라면 코드를 생성할 때 바로 평가하고 상수 값 9로 바로 대체할 수 있다. 그리고 만약 표현식에 루프가 있다면 루프를 순회할 때마다 저장하고 로프가 충분한 횟수만큼 실행되면 표현식을 검사하고 코드를 생성하는데 드는 오버헤드를 반환 받을 수 있을 것이다.

그리고 표기법이 문제를 표현할 수 있는 일반적인 방법을 제공한다는 것이 기본 생각이지만 표기법을 위한 컴파일러는 특정 계산의 세부사항을 위한 코드를 생성해 줄 수 있다.

실제 온 더 플라이 컴파일러를 만드는데 어떠한 것들이 필요한지를 설명하는 것은 특정 인스트럭션 집합의 상세와는 너무 동떨어진 것이긴 하나, 이러한 시스템이 어떻게 동작하는지에 대해 알아보는데 약간의 시간을 투자하는 것은 충분한 가치가 있다.

코드 생성에 대해 개략적으로 살펴봄으로써 실제 컴파일러에 사용되는 약간의 기술들을 얼핏 살펴보았으나 많은 부분들을 놓치고 지나갔으며, 또한 근래의 중앙처리장치의 복잡성으로 인해 발생되는 많은 이슈들을 회피하고 넘어갔다. 그러나 문제를 효과적으로 풀기 위한 특정 목적 코드를 만들어 봄으로써 프로그램이 문제에 대한 기술을 어떻게 분석할 수 있는지에 대해서는 예를 통해 알아보았다. 그리고 명백히 빠른 버전의 grep을 쓰거나, 자신만의 작은 언어를 창안하여 구현하거나, 또는 특정 목적 계산을 위해 최적화된 가상 머신을 설계하거나. 심지어 흥미 있는 언어를 위한 컴파일러를 쓰는 데에 이러한 아이디어들을 이용할 수 있을 것이다.