TECH 으로 돌아가기
TECH HACKER NEWS 오늘 8분 읽기 23 READS

Rust 컴파일은 왜 느리고 어떻게 빨라지고 있을까: 컴파일러 성능 개선 현황과 당장 써먹는 빌드 팁

'빌드 기다리는 시간'이라는 Rust의 오래된 숙제

Rust를 써보신 분이라면 한 번쯤 겪어보셨을 거예요. 코드 한 줄 고치고 cargo build를 눌렀는데 커피 한 잔 타올 시간이 지나도 빌드가 안 끝나는 경험이요. 실제로 Rust 공식 설문조사에서도 '컴파일 속도'는 해마다 가장 아쉬운 점 상위권에 들어요.

이 문제를 오랫동안 붙잡고 있는 사람 중 한 명이 Nicholas Nethercote인데요, Valgrind 개발자로도 잘 알려져 있고 몇 년째 rustc(Rust 컴파일러)의 성능을 개선해오고 있어요. 그가 주기적으로 쓰는 'How to speed up the Rust compiler' 시리즈의 2026년 9월판이 올라왔어요. 이 시리즈는 컴파일러가 그동안 어떻게, 얼마나 빨라졌는지 정리하는 일종의 '성능 일지'예요. 이번 기회에 Rust 컴파일이 왜 느린지, 컴파일러 팀은 어떻게 접근하는지, 우리는 당장 뭘 할 수 있는지까지 한 번에 정리해볼게요.

Rust 컴파일이 느린 진짜 이유

첫째는 제네릭의 단형화(monomorphization)예요. 이게 뭐냐면, Rust의 제네릭은 쓰이는 타입마다 코드를 복사해서 따로 만드는 방식이거든요. Vec<i32>와 Vec<String>을 쓰면 컴파일러가 각각을 위한 코드를 따로 찍어내요. 덕분에 실행할 때는 타입별로 최적화된 코드가 돌아가서 빠르지만, 컴파일러가 만들고 최적화해야 할 코드는 확 늘어나죠.

둘째는 LLVM 백엔드예요. rustc는 코드를 분석한 뒤 실제 기계어를 만드는 일을 LLVM에 맡기는데, LLVM은 최적화를 정말 잘하는 대신 시간이 꽤 걸려요. 특히 릴리스 빌드에서는 전체 컴파일 시간 중 큰 몫을 차지하는 경우가 많아요.

셋째는 크레이트 단위 컴파일과 링킹이에요. Rust는 크레이트(패키지) 하나를 하나의 컴파일 단위로 다뤄서, 거대한 크레이트 하나를 여러 조각으로 나눠 병렬 처리하기가 어려워요. 마지막에 결과물을 하나로 묶는 링킹 단계도 프로젝트가 커질수록 무거워지고요.

넷째는 매크로와 타입 검사예요. serde의 #[derive(Serialize)] 같은 프로시저 매크로는 컴파일 도중에 코드를 생성하는 작은 프로그램이에요. 편리하지만 많이 쓸수록 컴파일러가 처리할 코드도 늘어나요. 트레이트 해석이나 빌림 검사(borrow checking)에도 시간이 들고요.

컴파일러 팀은 어떻게 빠르게 만들고 있을까

rustc 성능 개선의 출발점은 측정이에요. perf.rust-lang.org라는 대시보드가 있는데, rustc에 PR이 머지될 때마다 실제 인기 크레이트를 포함한 벤치마크를 돌려서 컴파일 시간이 어떻게 변했는지 기록해요. 이때 실행 시간뿐 아니라 '실행된 CPU 명령어 수'처럼 측정할 때마다 들쭉날쭉하지 않은 지표도 같이 보는데요, 그래야 1%도 안 되는 작은 개선이나 성능 저하도 놓치지 않거든요. 하나하나는 작은 개선이지만 몇 년간 쌓이면서 체감 속도가 꽤 달라졌어요.

굵직한 프로젝트도 여럿 진행돼 왔어요. 파싱과 타입 검사 같은 컴파일러 앞단을 여러 스레드로 돌리는 병렬 프론트엔드는 나이틀리에서 -Z threads 옵션으로 실험할 수 있어요. 2025년 Rust 1.90부터는 x86_64 리눅스의 기본 링커가 LLD로 바뀌면서 링킹 시간이 크게 줄었고요. 디버그 빌드에서 LLVM 대신 훨씬 가벼운 Cranelift 백엔드를 쓰는 작업도 이어지고 있어요. 9월 글은 이런 흐름 속에서 최근 들어온 개선들을 정리하고 있으니, 세부 수치는 원문의 차트로 확인해보세요.

지금 당장 써먹을 수 있는 빌드 팁

컴파일러가 빨라지길 기다리는 것과 별개로 우리가 할 수 있는 것도 많아요. 먼저 cargo build --timings로 어디서 시간이 새는지부터 확인하세요. 크레이트별 빌드 시간이 HTML 리포트로 나와서 병목이 한눈에 보여요. 개발 중에 cargo check를 습관처럼 쓰는 것만으로도 차이가 커요. 기계어를 만들지 않고 타입 검사까지만 하거든요.

디버그 정보도 줄여볼 만해요. Cargo.toml의 [profile.dev]에 debug = "line-tables-only"나 debug = 0을 주면, 디버거를 자주 쓰지 않는 경우 빌드가 꽤 가벼워져요. 의존성도 점검해보세요. cargo tree로 의존성 트리를 살펴보고 안 쓰는 기본 기능을 default-features = false로 끄면 컴파일할 코드 자체가 줄어요.

큰 크레이트 하나를 여러 개로 쪼개면 병렬 빌드와 증분 컴파일 효율이 좋아져요. 제네릭이 너무 많이 복제되는 것 같다면 cargo-llvm-lines로 어떤 함수가 코드를 많이 만들어내는지 확인할 수 있고요. CI에서는 sccache로 빌드 결과를 캐싱하는 것도 효과적이에요.

다른 언어와 비교하면

Go는 처음부터 '빠른 컴파일'을 설계 목표로 삼아 언어 기능을 단순하게 유지했어요. 반대로 C++는 템플릿 때문에 Rust와 비슷한 고민을 오래 해왔고요. 결국 Rust의 컴파일 시간은 '실행 시 비용 없는 추상화(zero-cost abstraction)'와 강력한 컴파일 타임 검사를 얻는 대가예요. 실행할 때 치를 비용을 컴파일할 때 미리 치르는 셈이죠. 컴파일러 팀의 목표는 이 장점을 지키면서 그 대가를 최대한 줄이는 거고요.

한국 개발자에게 주는 시사점

국내에서도 백엔드, 인프라 도구, 블록체인, 임베디드 쪽에서 Rust를 쓰는 팀이 늘고 있어요. 팀이 커질수록 빌드 시간은 곧 개발 생산성이자 CI 비용이죠. 위 팁들은 대부분 설정 몇 줄로 바로 적용할 수 있으니, 이번 주에 --timings 리포트부터 한번 뽑아보세요. 그리고 Rust 버전을 꾸준히 올리기만 해도 공짜로 성능이 좋아진다는 점도 꼭 기억해두세요.

마무리

한 줄 정리: Rust 컴파일러는 꾸준한 측정과 작은 개선이 쌓이면서 빨라지고 있고, 우리도 설정 몇 줄로 빌드 시간을 확 줄일 수 있어요.

여러분 프로젝트의 Rust 빌드 시간은 어느 정도인가요? 효과를 크게 본 빌드 최적화 팁이 있다면 댓글로 공유해주세요!


🔗 출처: Hacker News

SOURCE · HACKER NEWS
원문 전체 보기 → https://nnethercote.github.io/2026/09/30/how-to-speed-up-the...
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...