실행 방식이 만드는 차이
컴파일한 프로그램이 빠를 수 있는 이유는 실행 중 수행해야 하는 일이 달라지기 때문이다. CPU가 직접 실행할 기계 코드를 미리 만들면 인터프리터가 중간 명령을 읽고 의미를 판별해 해당 동작으로 연결하는 비용을 줄일 수 있다. 반복 계산에서는 이런 비용이 누적된다. 인터프리터 역시 CPU에서 기계 코드로 실행되는 소프트웨어다.
최적화가 실행할 일을 줄인다
컴파일러는 결과를 유지하면서 실행할 일을 줄이거나 CPU에 맞는 코드로 바꿀 수 있다. 상수 계산, 불필요한 코드 제거, 함수 인라이닝, 반복문 변환 등이 예다. 적용할 수 있는 최적화는 언어의 의미, 컴파일러와 설정 및 대상 CPU에 따라 달라진다.
GCC 공식 문서는 최적화 수준에 따라 분석과 변환이 달라진다고 설명한다. 최적화를 거의 하지 않는 빌드와 최적화된 빌드를 구별해야 하며, 옵션을 높인다고 모든 프로그램이 더 빨라지는 것은 아니다.
컴파일과 해석은 함께 쓰일 수 있다
컴파일은 반드시 CPU 기계어를 출력하는 작업만 뜻하지 않는다. 다른 언어나 중간 표현으로 번역할 수도 있다. CPython은 Python 소스를 바이트코드로 컴파일하고 그 바이트코드를 실행한다. Python은 컴파일을 전혀 하지 않고 매번 소스 한 줄씩을 처음부터 번역한다는 설명은 부정확하다.
Java HotSpot은 인터프리터와 실행 중 컴파일을 조합한다. 자주 실행되는 코드에 대한 정보를 모아 기계 코드를 생성하고 최적화한다. 실행 전 컴파일인 AOT와 실행 중 컴파일인 JIT를 구분하면 언어를 두 종류로만 나눌 때 생기는 혼동을 줄일 수 있다.
실제 속도를 비교할 때
CPU 계산이 병목인 경우와 네트워크나 저장 장치의 응답을 기다리는 경우는 다르다. 전체 시간이 대기 시간에 의해 결정된다면 계산 부분의 개선 효과는 작을 수 있다. 알고리즘, 자료 구조, 라이브러리, 메모리 사용과 실행 환경도 영향을 준다.
같은 작업과 입력, 결과를 기준으로 측정하고 컴파일 시간이나 시작 시간을 포함하는지, JIT 준비가 끝난 뒤의 속도인지도 구별한다. 컴파일 언어라는 이름만으로 항상 더 빠르다고 결론 내릴 수는 없다.
관련 개념
컴파일러와 바이트코드는 코드 변환과 실행 과정을 이해하는 데 연결된다.