쿼리 기반 컴파일러, 정말 만능일까?
쿼리 기반 컴파일러(Query-based Compiler)는 증분 계산(Incremental Computation) 아이디어를 컴파일에 적용한 기술임
IDE 환경에서 100ms 이내의 응답 속도를 목표로, 코드 변경에 따른 빠른 컴파일(Fast Compilation)을 지원함
암호화(Encryption)와 같이 출력값 변화가 큰 경우, 쿼리 기반 컴파일러의 성능 한계(Performance Limitation)가 존재함
Zig 언어처럼 언어 설계를 통해 쿼리 사용을 최소화하는 직접적인 접근 방식(Direct Approach)이 대안으로 제시됨
쿼리 기반 컴파일러의 기본 원리
쿼리 기반 컴파일러는 입력 변경에 따라 영향받는 부분만 재계산(Recomputation)하는 증분 컴파일(Incremental Compilation) 방식을 사용한다. 이는 함수 호출 그래프(Function Call Graph)를 기반으로, 변경된 입력에서 루트 쿼리(Root Query)까지의 경로만 다시 계산하는 방식으로 구현된다. 특히, 입력 변경에도 결과가 변하지 않는 경우, 조기 종료(Early Cutoff)를 통해 불필요한 계산을 줄여 컴파일 속도(Compilation Speed)를 향상시킨다.
성능 병목 지점: 암호화 함수의 예시
암호화 함수와 같이 입력의 작은 변화에도 출력의 상당 부분이 바뀌는 경우, 쿼리 기반 컴파일러의 성능은 제한적이다. Avalanche Property로 인해 출력 변경 작업 자체가 O(N)의 시간 복잡도를 가지게 되기 때문이다. 즉, 변경된 부분만 빠르게 업데이트하는 것이 불가능해지며, 증분 컴파일의 이점(Benefit of Incremental Compilation)을 살리기 어렵다. 따라서, 쿼리 기반 컴파일러는 특정 유형의 연산에 적합하지 않을 수 있다.
언어 설계와 컴파일러 아키텍처의 관계
저자는 쿼리 기반 컴파일러 대신, 언어 설계를 통해 컴파일 과정을 단순화하는 방식을 선호한다. Zig 언어의 경우, 각 파일을 독립적으로 파싱(Parsing)하고 이름 확인(Name Resolution) 또한 파일 단위로 처리하여 쿼리 사용을 최소화한다. 이는 Zig AstGen: AST => ZIR 과정을 통해 AST를 ZIR로 직접 변환하는 방식으로 구현된다. 반면, Rust는 매크로(Macro) 확장이 필요하여 쿼리 기반 컴파일러가 불가피하다.
IDE 아키텍처와 컴파일러의 역할
저자는 IDE의 응답성을 높이기 위해 세 가지 아키텍처를 제시하며, 쿼리 기반 컴파일러를 세 번째 대안으로 언급한다. 쿼리 기반 컴파일러는 빠른 코드 변경 반영(Fast Code Change Reflection)을 가능하게 하지만, 언어의 특성에 따라 성능에 제약이 있을 수 있다. 따라서, 언어 설계 단계에서부터 컴파일러의 효율성을 고려하는 것이 중요하며, 직접적인 접근 방식(Direct Approach)을 통해 컴파일 과정을 최적화할 수 있다.