성능 분석을 위한 PyTorch Profiler 활용
모델 학습 속도를 최적화하기 전에는 코드의 실제 실행 시간을 정확히 파악해야 합니다. torch.autograd.profiler는 CPU 및 GPU 성능 정보를 세밀하게 측정할 수 있는 강력한 도구입니다. 주요 측정 항목은 다음과 같습니다:
- CPU 연산 지연 시간
- CUDA 커널 실행 시간
- 메모리 사용 추이
사용 방법은 간단합니다. 학습 루프를 프로파일러 컨텍스트 내에 포함하면 됩니다:
import torch.autograd.profiler as profiler
with profiler.profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
on_trace_ready=profiler.tensorboard_trace_handler('./logs')
) as prof:
train_model(args)
이후 tensorboard를 통해 수집된 트레이스를 시각화할 수 있습니다. 중요한 설정은 activities와 profile_memory 옵션입니다. 가능한 한 적은 기능만 활성화하는 것이 성능 부담을 줄이는 핵심입니다. 예를 들어, 단순히 CUDA 커널 시간만 분석하고 싶다면, CPU 분석은 비활성화하세요.
또한, 각 코드 블록의 의미를 명확히 하기 위해 record_function을 사용해 태그를 추가하는 것이 좋습니다. 이는 프로파일링 결과에서 구분이 쉬워집니다:
with profiler.record_function("forward_pass"):
output = model(**batch_data)
with profiler.record_function("backward_step"):
loss.backward()
optimizer.step()
더 세밀한 분석을 원한다면 계층별로 분류할 수도 있습니다:
with profiler.record_function("attention_layer:qkv_projection"):
q, k, v = compute_qkv(inputs)
with profiler.record_function("attention_layer:softmax_attention"):
attn_weights = softmax(q @ k.T / sqrt(d_model))
트레이스 분석으로 핵심 지점 식별 트레이스 파일을 열어 보면, 학습 과정의 주요 단계가 명확히 드러납니다:
- 데이터 로드
- 전방전달 (forward pass)
- 역방향전달 (backward pass)
특히 역방향 전달은 별도 스레드에서 처리되며, 이는 쉽게 식별 가능합니다. 이 스레드에서 긴 지연이 발생한다면, 학습 효율성이 떨어질 수 있습니다.
데이터 로딩 최적화 GPU가 대기 상태인 것은 자원 낭비입니다. 데이터 로딩과 계산은 병렬 수행이 가능하므로, 이를 유도해야 합니다.
프로파일링 결과에서 GPU SM 활용률이 0인 구간을 확인하면, 데이터 준비가 느린 부분을 찾을 수 있습니다. 이를 해결하기 위한 방법은 다음과 같습니다:
DataLoader의num_workers를 증가시켜 다중 프로세스로 데이터 전처리 수행IterableDataset사용 시get_worker_info()를 활용해 각 워커가 겹치지 않는 데이터를 처리하도록 조정
필요하다면 multiprocessing 모듈을 직접 사용해 고도화된 데이터 파이프라인을 구현할 수도 있습니다.
메모리 할당 최적화
PyTorch는 CUDA 장치에서 메모리 할당 시 내부 캐시 기반 할당기를 사용합니다. 이는 cudaMalloc/cudaFree 호출 횟수를 줄여 성능을 향상시킵니다. 일반적으로는 이전에 할당했던 메모리 블록을 재사용합니다.
하지만 입력 길이가 변동이 큰 경우, 중간 텐서 크기가 달라져 할당기에서 적절한 블록을 찾기 어려울 수 있습니다. 이때 할당기는 기존 메모리를 해제하고 다시 캐시를 구성해야 하며, 이 과정에서 많은 cudaMalloc 호출이 발생합니다.
이 현상을 인식하려면 트레이스의 메모리 분석 탭을 확인하세요. 만약 할당기의 예약 메모리 선이 지속적으로 상하좌우로 진동한다면, 메모리 재사용이 제대로 이루어지지 않고 있음을 의미합니다. 반면, 안정적인 수준의 선은 정상적인 작동을 나타냅니다.
이러한 문제를 피하려면, 가능한 한 일관된 입력 크기를 유지하거나, 미리 할당된 버퍼를 재사용하는 방식을 고려하세요.