Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

컴파일러 프로파일링

이 절에서는 컴파일러를 프로파일링하여 컴파일러가 어디에서 시간을 소비하는지 알아내는 방법을 설명합니다.

측정하려는 대상에 따라 여러 가지 접근 방식이 있습니다.

  • PR이 컴파일러 성능을 개선하는지 저하시키는지 확인하려면, 벤치마크 실행을 요청하는 방법을 다루는 rustc-perf 장을 참고하십시오.

  • rustc가 어디에서 시간을 소비하는지 중간 수준에서 높은 수준까지의 개요를 보려면 다음을 참고하십시오.

    • -Z self-profile 플래그와 measureme 도구는 쿼리 기반의 프로파일링 방식을 제공합니다. 자세한 내용은 해당 문서를 참고하십시오.
  • 함수 수준의 성능 데이터가 필요하거나 위의 접근 방식보다 더 자세한 정보가 필요하다면 다음을 참고하십시오.

    • perf 같은 네이티브 코드 프로파일러를 사용하는 것을 고려하십시오.
    • 또는 나노초 단위의 정밀도와 모든 기능을 갖춘 그래픽 인터페이스가 필요하다면 tracy를 사용하십시오.
  • 크레이트 그래프의 컴파일 시간을 보기 좋게 시각화하려면 cargo의 --timings 플래그를 사용할 수 있습니다. 예를 들면 cargo build --timings와 같습니다. ./x build compiler --timings를 사용하면 컴파일러 자체에도 이 플래그를 사용할 수 있습니다.

  • 메모리 사용량을 프로파일링하려면 사용 중인 운영 체제에 따라 다양한 도구를 사용할 수 있습니다.

cargo-llvm-lines로 rustc의 부트스트랩 시간 최적화하기

cargo-llvm-lines를 사용하면 제네릭 함수의 모든 인스턴스화에 걸친 LLVM IR 줄 수를 셀 수 있습니다. rustc 컴파일 시간의 대부분이 LLVM에서 소비되므로, LLVM에 전달되는 코드의 양을 줄이면 rustc 컴파일이 빨라진다는 것이 기본 발상입니다.

다소 독자적인 rustc 빌드 과정과 함께 cargo-llvm-lines를 사용하려면 -C save-temps를 사용하여 필요한 LLVM IR을 얻을 수 있습니다. 이 옵션은 컴파일 중에 생성된 임시 작업 산출물을 보존합니다. 그중에는 최적화 파이프라인의 입력을 나타내는 LLVM IR이 있으며, 이는 우리의 목적에 이상적입니다. 이는 LLVM 비트코드 형식으로 *.no-opt.bc 확장자를 가진 파일에 저장됩니다.

사용 예시:

cargo install --locked cargo-llvm-lines
# On a normal crate you could now run `cargo llvm-lines`, but `x` isn't normal :P

# Do a clean before every run, to not mix in the results from previous runs.
./x clean
env RUSTFLAGS=-Csave-temps ./x build --stage 0 compiler/rustc

# Single crate, e.g., rustc_middle. (Relies on the glob support of your shell.)
# Convert unoptimized LLVM bitcode into a human readable LLVM assembly accepted by cargo-llvm-lines.
for f in build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/rustc_middle-*.no-opt.bc; do
  ./build/x86_64-unknown-linux-gnu/llvm/bin/llvm-dis "$f"
done
cargo llvm-lines --files ./build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/rustc_middle-*.ll > llvm-lines-middle.txt

# Specify all crates of the compiler.
for f in build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/*.no-opt.bc; do
  ./build/x86_64-unknown-linux-gnu/llvm/bin/llvm-dis "$f"
done
cargo llvm-lines --files ./build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/*.ll > llvm-lines.txt

컴파일러에 대한 출력 예시:

  Lines            Copies          Function name
  -----            ------          -------------
  45207720 (100%)  1583774 (100%)  (TOTAL)
   2102350 (4.7%)   146650 (9.3%)  core::ptr::drop_in_place
    615080 (1.4%)     8392 (0.5%)  std::thread::local::LocalKey<T>::try_with
    594296 (1.3%)     1780 (0.1%)  hashbrown::raw::RawTable<T>::rehash_in_place
    592071 (1.3%)     9691 (0.6%)  core::option::Option<T>::map
    528172 (1.2%)     5741 (0.4%)  core::alloc::layout::Layout::array
    466854 (1.0%)     8863 (0.6%)  core::ptr::swap_nonoverlapping_one
    412736 (0.9%)     1780 (0.1%)  hashbrown::raw::RawTable<T>::resize
    367776 (0.8%)     2554 (0.2%)  alloc::raw_vec::RawVec<T,A>::grow_amortized
    367507 (0.8%)      643 (0.0%)  rustc_query_system::dep_graph::graph::DepGraph<K>::with_task_impl
    355882 (0.8%)     6332 (0.4%)  alloc::alloc::box_free
    354556 (0.8%)    14213 (0.9%)  core::ptr::write
    354361 (0.8%)     3590 (0.2%)  core::iter::traits::iterator::Iterator::fold
    347761 (0.8%)     3873 (0.2%)  rustc_middle::ty::context::tls::set_tlv
    337534 (0.7%)     2377 (0.2%)  alloc::raw_vec::RawVec<T,A>::allocate_in
    331690 (0.7%)     3192 (0.2%)  hashbrown::raw::RawTable<T>::find
    328756 (0.7%)     3978 (0.3%)  rustc_middle::ty::context::tls::with_context_opt
    326903 (0.7%)      642 (0.0%)  rustc_query_system::query::plumbing::try_execute_query

이 방법은 증분 컴파일이나 ./x check와 함께 동작하지 않는 것으로 보이므로, rustc를 아주 많이 컴파일하게 될 것입니다. 견딜 만하도록 bootstrap.toml에서 몇 가지 설정을 변경할 것을 권장합니다.

# A debug build takes _a third_ as long on my machine,
# but compiling more than stage0 rustc becomes unbearably slow.
rust.optimize = false

# We can't use incremental anyway, so we disable it for a little speed boost.
rust.incremental = false
# We won't be running it, so no point in compiling debug checks.
rust.debug = false

# Using a single codegen unit gives less output, but is slower to compile.
rust.codegen-units = 0  # num_cpus

llvm-lines 출력은 여러 옵션의 영향을 받습니다. rust.optimize = false는 출력을 2.1GB에서 3.5GB로 늘리고, codegen-units = 0은 4.1GB로 늘립니다.

MIR 최적화는 영향이 거의 없습니다. 기본값인 RUSTFLAGS="-Z mir-opt-level=1"과 비교하면 레벨 0은 0.3GB를 늘리고 레벨 2는 0.2GB를 줄입니다. 2022년 7월 기준으로 인라이닝은 LLVM 및 GCC 코드 생성 백엔드에서 이루어지며, Cranelift 백엔드에서만 이루어지지 않습니다.