Skip to content

프로파일링

디버그 정보가 포함된 release 모드로 oxlint 빌드

프로파일링을 위해 oxlint 바이너리를 디버그 정보가 켜진 release 설정으로 빌드해야 합니다. cargo build--profile release-with-debug를 넘기면 됩니다.

bash
cargo build --profile release-with-debug --bin oxlint

빌드 후 바이너리 위치는 ./target/release-with-debug/oxlint입니다. 프로파일링에는 이 바이너리를 사용합니다.

CPU - Samply

Samply는 Firefox Profiler를 UI로 쓰는 CLI CPU 프로파일러입니다. macOS와 Linux에서 동작합니다.

oxlint와 함께 쓰려면 samply record 뒤에 oxlint 명령과 인자를 붙입니다.

bash
samply record ./target/release-with-debug/oxlint .

프로파일링 체험을 개선하려면 다음을 고려할 수 있습니다.

  • oxlint: --silent는 진단 출력을 줄여 프로파일이 더 집중되게 만듭니다.
  • oxlint: --threads 1은 단일 스레드로 실행해(느려지지만) 단일 스레드 성능 분석이 쉬워집니다.
  • samply record: --rate <숫자>로 샘플링 주파수를 높입니다. 기본은 1000Hz(1ms)이며, 높이면 더 자세하지만 파일이 커집니다.

예: 0.1ms 샘플링으로 단일 스레드 oxlint 실행:

bash
samply record --rate 10000 ./target/release-with-debug/oxlint --silent --threads 1 .

CPU - Mac Xcode Instruments

cargo instruments는 Mac Xcode Instruments와 연동할 때 적합합니다.

아래 안내는 cargo instruments 절차를 재현합니다.

먼저 Xcode Instruments 커맨드라인 도구를 설치합니다.

bash
xcode-select --install

이미 빌드하지 않았다면 위의 oxlint 빌드 단계를 수행합니다.

내부적으로 cargo instruments는 다음과 같은 xcrun xctrace 호출과 같습니다.

bash
xcrun xctrace record --template 'Time Profile' --output . --launch -- /path/to/oxc/target/release-with-debug/oxlint

위 명령을 실행하면 대략 다음과 같은 출력이 나옵니다.

Starting recording with the Time Profiler template. Launching process: oxlint.
Ctrl-C to stop the recording
Target app exited, ending recording...
Recording completed. Saving output file...
Output file saved as: Launch_oxlint_2023-09-03_4.41.45 PM_EB179B85.trace

trace 파일을 연 다음 open Launch_oxlint_2023-09-03_4.41.45\ PM_EB179B85.trace처럼 엽니다.

Top-down 트레이스를 보려면:

  1. 상단 패널에서 CPUs를 클릭합니다.
  2. 왼쪽 입력란에서 x를 누르고 Time Profiler를 선택합니다.
  3. 하단 패널에서 "Call Tree"를 누르고 "Invert Call Tree"를 켠 뒤, 스레드별 분리는 끕니다.

메모리·디스크는 --template 'Allocations', --template 'File Activity'를 사용합니다.

L1/L2 캐시 미스, 사이클·명령 수, 분기 예측 등 더 세부적인 CPU 프로파일링은 사용자 정의 "CPU Counters" 템플릿이 필요합니다.

  1. Instruments를 열고 "CPU Counters" 템플릿을 선택합니다.
  2. "CPU Counters" 설정에서:
    1. "High Frequency Sampling"을 켭니다.
    2. 그 아래 더하기 아이콘으로 이벤트 유형을 추가합니다. 예:
      • Cycles — 함수당 대략적인 CPU 사이클
      • Instructions — 함수당 대략적인 명령 수와 사이클
      • L1D_CACHE_MISS_LD — 메모리 로드로 인한 L1 캐시 미스 횟수
  3. 관심 이벤트를 켠 뒤 "File > Save as Template ..."으로 저장하고 이름을 붙입니다.
  4. xcrun xctrace record --template 'My Custom CPU Counters' --output . --launch -- /path/to/oxc/target/release-with-debug/oxlint처럼 --template에 템플릿 이름을 넘깁니다.

힙 할당 - dhat

dhat는 메모리 누수를 찾고 힙 할당 패턴을 분석하는 데 도움이 되는 힙 프로파일러입니다.

설정

Cargo.toml에 dhat 의존성을 추가합니다.

toml
[dependencies]
dhat = "0.3"

바이너리 크레이트 상단에 전역 할당자를 둡니다.

rust
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;

프로파일링

프로파일링할 스코프에 Profiler를 만듭니다. 생성 시점부터 드롭될 때까지의 할당이 기록됩니다.

rust
fn main() {
    let _profiler = dhat::Profiler::new_heap();
    // 여기 코드 — 모든 힙 할당이 추적됨
}

특정 함수만 추적하려면 해당 함수에 _profiler를 둡니다.

rust
fn my_function() {
    let _profiler = dhat::Profiler::new_heap();
    // 이 함수 스코프 안의 할당만 추적
}

Profiler가 드롭되면 자동으로 dhat-heap.json이 생성됩니다.

프로파일 읽기

실행 후 작업 디렉터리에 dhat-heap.json이 생깁니다.

분석 절차:

  1. 브라우저에서 dhat 뷰어 https://nnethercote.github.io/dh_view/dh_view.html 을 엽니다.
  2. dhat-heap.json을 불러옵니다.
  3. "Sort metrics"에서 지표를 고릅니다.
    • "At t-gmax (bytes)": 피크 힙 사용 시점의 할당입니다. 최대 힙에 무엇이 쓰였는지 볼 때 사용합니다.
    • "At t-end (bytes)": Profiler가 종료되기까지 해제되지 않은 메모리입니다. 누수 후보 파악에 유용합니다.
    • "Total (bytes)": 실행 전체에 걸쳐 할당된 바이트 합입니다. 나중에 free되더라도 할당이 많은 코드를 찾을 때 사용합니다.

고급: Profiler 수명 제어

정리 단계를 제외하고 코어만 측정하려면 수명을 직접 관리합니다.

rust
struct MyApp {
    profiler: Option<dhat::Profiler>,
    // 기타 필드
}

impl MyApp {
    fn close(&mut self) {
        // 정리 전 힙 상태를 남기기 위해 여기서 profiler 드롭
        self.profiler = None;
        // 정리 코드
    }
}

이 패턴으로 실행 중 특정 시점에 어떤 구조체가 메모리를 들고 있는지 파악하기 쉬워집니다.