Skip to content

프로파일링

릴리스 모드에서 디버그 정보를 포함해 oxlint 컴파일하기

프로파일링을 위해서는 oxlint 바이너리를 디버그 정보가 활성화된 릴리스 모드로 컴파일해야 합니다. 이 작업은 cargo build--profile release-with-debug 옵션을 전달하여 수행할 수 있습니다:

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

빌드 후, 바이너리는 ./target/release-with-debug/oxlint에 위치합니다. 이 바이너리는 프로파일링에 사용되어야 하는 대상입니다.

CPU - Samply

Samply는 Firefox 프로파일러를 사용하는 콘솔 기반의 CPU 프로파일러입니다. macOS 및 Linux에서 작동합니다.

oxlint와 함께 Samply를 사용하려면 samply record 명령어 다음에 oxlint 명령어와 인수를 입력하세요:

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

프로파일링 경험을 개선하기 위해 아래 옵션들을 고려해 보세요:

  • oxlint: --silent 옵션은 진단 출력을 억제하고 프로파일링에 더 집중되게 합니다.
  • oxlint: --threads 1 옵션은 리터를 단일 스레드로 실행합니다. 느리지만 단일 스레드 성능 분석에 더 용이합니다.
  • samply record: --rate <number> 옵션은 더 높은 빈도로 프로파일링을 샘플링합니다. 기본값은 1000Hz(1ms)이며, 이를 늘리면 더 세밀한 정보를 제공하지만 더 큰 프로파일 파일이 생성됩니다.

예를 들어, 0.1ms 샘플링 주기로 oxlint를 단일 스레드로 실행하는 경우:

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

CPU - Mac Xcode Instruments

cargo instruments는 맥의 Xcode Instruments를 연결하기 위한 추천 도구입니다.

아래 지침은 cargo instruments의 절차를 재현합니다.

먼저, Xcode Instruments 커맨드라인 도구를 설치하세요:

bash
xcode-select --install

그런 다음, 아직 하지 않았다면 oxlint 바이너리가 컴파일되었는지 확인하세요.

내부적으로 cargo instrumentsxcrun xctrace 명령어를 호출하며, 이는 다음과 같습니다:

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

위 명령어를 실행하면 다음과 같은 출력이 발생합니다:

시간 프로파일러 템플릿으로 녹화 시작. 프로세스 실행: oxlint.
녹화 중지하려면 Ctrl-C를 누르세요.
대상 앱 종료됨, 녹화 종료...
녹화 완료. 출력 파일 저장 중...
출력 파일 저장됨: Launch_oxlint_2023-09-03_4.41.45 PM_EB179B85.trace

추적 파일을 열기 위해 open Launch_oxlint_2023-09-03_4.41.45\ PM_EB179B85.trace를 실행하세요.

상향식 추적을 보기 위해 다음 단계를 따르세요:

  1. 상단 패널에서 CPUs를 클릭합니다.
  2. 왼쪽 입력 상자에서 x를 클릭한 후, 시간 프로파일러(Time Profiler)를 선택합니다.
  3. 하단 패널에서 "콜 트리"(Call Tree)를 클릭하고, "역방향 콜 트리"(Invert Call Tree)를 활성화하고 스레드별 분리 기능은 비활성화합니다.

메모리 및 디스크 작업 분석에는 --template 'Allocations'--template 'File Activity'를 사용하세요.

더 정교한 CPU 프로파일링(예: L1/L2 캐시 미스, 사이클 및 명령어 카운트, 분기 예측 정보 등)을 위해서는 사용자 정의 "CPU 카운터" 템플릿을 사용해야 합니다:

  1. Instruments를 열고 "CPU 카운터" 템플릿을 선택합니다.
  2. "CPU 카운터" 설정에서:
    1. "고주파 샘플링"(High Frequency Sampling) 옵션을 활성화합니다.
    2. "고주파 샘플링" 옵션 아래에서 플러스 아이콘을 클릭하고 이벤트 유형을 선택합니다. 몇 가지 제안되는 이벤트 유형:
      • 사이클(Cycles): 각 함수에서 소비된 총 프로세서 사이클 수를 확인할 수 있습니다.
      • 명령어(Instructions): 각 함수에서 실행된 총 명령어 수와 그에 필요한 사이클 수를 확인할 수 있습니다.
      • L1D_CACHE_MISS_LD: 메모리에서 데이터를 로드할 때 발생한 L1 캐시 미스 횟수를 카운트합니다.
  3. 관심 있는 이벤트를 활성화한 후, "파일 > 템플릿으로 저장..."을 통해 템플릿을 저장하고 이름을 부여합니다.
  4. 이제 xctrace에 이 템플릿 이름을 전달하여 사용할 수 있습니다: xcrun xctrace record --template '내 맞춤형 CPU 카운터' --output . --launch -- /path/to/oxc/target/release-with-debug/oxlint

힙 할당 - dhat

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

설정

Cargo.toml에 dhat을 의존성으로 추가하세요:

toml
[dependencies]
dhat = "0.3"

그런 다음, 바이너리 크레이트의 최상위에 전역 할당자를 추가하세요:

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

프로파일링

프로파일링하고자 하는 범위 내에서 프로파일러를 생성하세요. 프로파일러는 생성된 시점부터 파괴될 때까지의 할당을 추적합니다:

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

또한 특정 함수 내에서만 메모리 할당을 추적하기 위해 _profiler를 함수에 추가할 수도 있습니다:

rust
fn my_function() {
    let _profiler = dhat::Profiler::new_heap();
    // 이 함수 범위 내의 할당만 추적됨
}

프로파일러는 파괴될 때 자동으로 dhat-heap.json 파일을 생성합니다.

프로파일 로딩 및 읽기

프로그램을 실행한 후, 현재 작업 디렉토리에 dhat-heap.json 파일이 생성됩니다.

프로파일을 분석하려면 다음을 수행하세요:

  1. dhat 뷰어를 열어주세요.
  2. dhat-heap.json 파일을 불러옵니다.
  3. "정렬 기준 메트릭"에서 다양한 메모리 사용 사항을 분석할 수 있는 메트릭을 선택하세요:
    • "최고 메모리 사용 시점 (바이트)": 최대 메모리 사용 시점에서의 할당량을 표시합니다. 프로그램이 최대 힙 크기에 도달했을 때 가장 많은 메모리를 차지하는 부분을 파악하는 데 유용합니다.
    • "종료 시점 (바이트)": 프로파일러가 파괴되기 전에 해제되지 않은 메모리를 표시합니다. 이는 특히 메모리 누수를 식별하는 데 매우 유용하며, 프로파일링 범위의 끝에서 남아 있는 할당 항목을 드러냅니다.
    • "총합 (바이트)": 전체 실행 기간 동안 할당된 총 바이트 수를 표시합니다. 메모리가 나중에 해제되더라도 어떤 코드 부분이 가장 많은 할당을 수행하는지 파악하는 데 사용할 수 있습니다.

고급 기법: 프로파일러 수명 조절

프로파일링이 언제 종료되는지를 더 정밀하게 제어하기 위해 프로파일러의 수명을 명시적으로 관리할 수 있습니다. 예를 들어, 핵심 로직만 프로파일링하고 정리 과정은 제외하려는 경우:

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

impl MyApp {
    fn close(&mut self) {
        // 정리 전에 프로파일러를 파괴하여 힙 상태를 캡처
        self.profiler = None;
        // 정리 코드
    }
}

이 패턴은 프로그램 실행 중 특정 시점에서 어떤 데이터 구조가 메모리를 보유하고 있는지를 파악하는 데 도움이 됩니다.