Rust와 C++의 컴파일 시간 비교하기
(quick-lint-js.com)다른 곳에서 재밌는 글을 읽게 되어 소개합니다.
Rust와 C++가 여러 면에서 비교 대상이 되곤 합니다. 그러나 컴파일 시간을 직접적으로 비교하기엔 같은 프로젝트가 두 언어로 모두 작성된 경우가 드물어서 힘들었습니다. quick-lint-js는 C++로 작성된 프로젝트에서 일부를 Rust로 재작성하여 컴파일 시간을 비교했다고 합니다. 다만 컴파일 환경에서 Windows는 빠졌습니다.
포팅하는 기준
- 3자 라이브러리 제외
- Linux 및 macOS에서 작업
- 광범위한 test suite
- FFI, 포인터, 표준 및 직접 작성한 컨테이너, 유틸리티 클래스 및 함수, I/O, 동시성, 제네릭, 매크로, SIMD, 상속, ...
결론
- Rust의 컴파일 시간은 C++과 비슷하거나 느림(적어도 이 프로젝트에서는)
- Rust가 C++보다 더 많은 코드를 작성해야 함
- 전체(Full) 빌드에서 C++이 비슷하거나 빠름
- 증분(Incremental) 빌드에서는 짧을 때도 있고 길 때도 있음(아주 긴 경우도 있음)
- quick-lint-js의 나머지는 Rust로 포팅하지 않기로 결정(빌드 시간 개선이 있다면 할 수도?)
1번 컴파일 이후 c++의 디펜던시가 많이 걸린 h가 아닌 단일 cpp 파일들을 수정하면 컴파일이 금새 되는데, rust는 어떤지 궁금하네요
러스트 문서의 자주 묻는 질문란에 관련 내용이 자세히 설명되어 있던데 공유해봅니다
====================================
Rust 컴파일이 느린 것 같습니다. 왜 그런 건가요?
코드를 기계어로 번역하고 최적화를 하기 때문입니다. Rust는 효율적인 기계어로 컴파일되는 고수준 추상화를 제공하고, 이 번역 과정은 특히 최적화를 할 경우 시간이 걸리게 마련입니다.
그러나 Rust의 컴파일 시간은 생각보다는 나쁜 편은 아니며, 앞으로 더 개선될 거라고 믿을 이유가 있습니다. C++와 Rust로 비슷한 크기의 프로젝트를 비교해 보면 전체 프로젝트를 컴파일하는 시간은 일반적으로 비슷하다고 봅니다. Rust 컴파일이 느리다고 느끼는 주된 원인은 C++와 Rust가 컴파일 모델이 다르다는 점, 즉 C++의 컴파일 단위는 한 파일이지만 Rust는 여러 파일로 이루어진 크레이트라는 것 때문입니다. 따라서 개발 도중에 C++ 파일 하나를 고치면 Rust에 비해 컴파일 시간이 훨씬 줄어들 수 있습니다. 현재 Rust 컴파일러를 리팩토링해서 증분 컴파일을 가능하게 하려는 대형 작업이 진행 중이며, 완료되면 Rust에서도 C++ 모델과 같이 컴파일 시간이 개선될 것입니다.
컴파일 모델과는 별개로, Rust의 언어 설계에는 컴파일 시간에 영향을 미치는 요소가 여럿 있습니다.
먼저 Rust는 비교적 복잡한 타입 시스템을 가지고 있고, 실행 시간에 Rust를 안전하게 만들기 위한 제약 사항을 강제하는 데 무시할 수 없는 컴파일 시간을 사용해야 합니다.
두번째로 Rust 컴파일러에는 오래된 기술 부채가 있으며, 특히 생성되는 LLVM IR의 품질이 좋지 못하기 때문에 LLVM이 시간을 들여 이를 “고쳐야” 합니다. 미래에는 MIR 기반 최적화 및 번역 단계가 Rust 컴파일러가 LLVM에 가하는 부하를 줄여 줄지도 모릅니다.
세번째로 Rust가 코드 생성에 LLVM을 쓰는 것은 양날의 검이라는 점입니다. LLVM 덕분에 Rust는 세계구급 런타임 성능을 보여 주지만, LLVM은 컴파일 시간에 촛점을 맞추지 않은 거대한 프레임워크이며 특히 품질이 낮은 입력에 취약합니다.
마지막으로 Rust가 일반화(제너릭) 타입을 C++와 비슷하게 단형화(monomorphise)하는 전략은 빠른 코드를 생성하지만, 다른 번역 전략에 비해 상당히 많은 코드를 생성해야 한다는 문제가 있습니다. 이 코드 팽창은 트레이트 객체를 써서 동적 디스패치와 장단을 교환할 수 있습니다.
확실한건 러스트 프로젝트들이 빌드하면서 디스크는 엄청 먹더군요. 라이브러리 백여개 가져다 쓴 프로젝트가 어느새 2GB 정도 쓰는 것 보고 깜짝 놀랐었던 기억이...