GPU 프로파일링 불꽃 그래프는 GPU 메모리를 가장 많이 사용하는 함수를 보여 줍니다. 이 문서에서는 AKS(Azure Kubernetes Service) GPU 프로파일링 도구에서 생성된 불꽃 그래프를 해석하는 방법을 설명합니다.
Important
AKS 미리 보기 기능은 셀프 서비스에서 사용할 수 있습니다(옵트인 방식). 미리 보기는 "있는 그대로" 및 "사용 가능한 상태로" 제공되며 서비스 수준 계약 및 제한적 보증에서 제외됩니다. AKS 미리 보기의 일부는 고객 지원팀에서 최선을 다해 지원합니다. 따라서 이러한 기능은 프로덕션 용도로 사용할 수 없습니다. 자세한 내용은 다음 지원 문서를 참조하세요.
시작하기 전에 GPU 프로파일링을 설정하고 프로파일링 데이터를 파이로스코프 및 Grafana에서 사용할 수 있는지 확인합니다.
메모
프로파일러가 워크로드를 분석하고 기호를 확인하는 동안 첫 번째 프로파일링 실행은 모델 로드와 같은 시작 작업의 기호를 포함하여 모든 기호를 표시하지 않을 수 있습니다. 초기 분석 후 워크로드를 다시 실행하여 후속 프로필에서 이러한 기호를 캡처합니다.
화염 그래프 이해
화염 그래프는 프로파일된 호출 스택을 시각화합니다. 각 막대는 함수를 나타내고 누적 막대는 호출 체인 또는 다른 함수라고 하는 함수를 표시합니다. 각 막대의 너비는 해당 함수를 통해 흐르는 CPU 시간 또는 할당된 GPU 메모리와 같은 측정된 리소스의 양을 나타냅니다.
키 규칙: 막대가 넓을수록 측정된 리소스가 해당 함수를 통해 더 많이 흐릅니다.
다음 샘플 화염 그래프는 vLLM 유추 워크로드에서 캡처됩니다.
Tip
Grafana의 플레임 그래프 패널에서 모든 그룹 펼치기를 사용하면 전체 호출 스택을 접지 않고 볼 수 있습니다. 검색 상자를 사용하여 특정 함수 또는 키워드를 찾습니다. 패널이 다시 축소되지 않도록 하려면 확장된 불꽃 그래프를 검사하는 동안 Grafana 대시보드의 오른쪽 위 모서리에서 새로 고침 간격을 Off 로 설정하여 대시보드 자동 새로 고침을 일시 중지합니다.
Tip
포커스 블록을 사용하여 특정 할당 경로에 집중합니다.
기호 읽기
화염 그래프 레이블은 다음 규칙을 따릅니다.
| 기호 형식 | Meaning |
|---|---|
Foo > bar |
class Foo: method def bar() |
Foo > __init__ |
클래스 생성자 Foo |
bar (단독으로) |
독립 실행형 def bar() 함수 |
<interpreter trampoline> |
CPython 오버헤드. 이 기호는 무시할 수 있습니다. |
<raw-address>, 예를 들어, 0x7f151 |
사용할 수 있는 Python 기호가 없는 네이티브 C 또는 CUDA 코드입니다. |
예는 다음과 같습니다.
-
GPUModelRunner/_allocate_kv_cache_tensors: 클래스의 메서드입니다. 이 기호는class GPUModelRunner: def _allocate_kv_cache_tensors(self)로 읽습니다. -
LlamaMLP/__init__: 개체를 만들 때 호출되는 생성자입니다LlamaMLP(...). -
_compile_fx_inner: 클래스 내에 없는 독립 실행형 모듈 수준 함수입니다.
자체 할당과 총 할당 비교 이해하기
Self 및 Total 값을 사용하여 리소스가 사용되는 위치를 결정합니다.
- Total 은 함수에서 사용하는 리소스와 호출하는 모든 리소스입니다. 함수는 총합이 클 수 있지만 호출 체인의 일부이기 때문에 아무 것도 할당하지 않습니다.
- 자체 함수는 자식 함수를 제외하고 함수에서 직접 사용하는 리소스입니다. 높은 자체 값은 이 함수가 리소스를 사용하는 위치임을 의미합니다.
예를 들어 GPUModelRunner._allocate_kv_cache_tensors의 자체 할당이 55.1GB라면, 키-값(KV) 캐시 텐서를 만들기 위해 torch.empty()를 호출하는 함수입니다.
다음 탐색 기술을 사용합니다.
- 리프 노드로 시작합니다. 이 노드는 그 위에 아무 것도 없는 막대입니다. 전체 너비는 자체 할당을 나타냅니다.
- 할당의 원인을 찾을 때 자체 할당량이 0인 너비가 넓은 막대는 건너뜁니다. 이러한 함수는 다른 함수에 대한 호출을 오케스트레이션합니다.
- 잠재적인 최적화 대상으로 삼을 수 있는, 자체 할당이 높은 넓은 막대를 조사하세요.
가장 큰 리소스 소비자 찾기
다음 단계를 사용하여 핫스팟을 식별합니다.
그래프 위쪽에서 가장 넓은 막대를 확인합니다. 이러한 리프 함수는 메모리를 할당합니다. 막대가 넓을수록 더 많은 메모리를 사용합니다.
자체 할당과 총 할당을 확인합니다. 아래쪽
self: 0의 넓은 막대가 호출 체인의 일부입니다. 자체 할당량이 높은 막대를 찾을 때까지 위로 따라 올라가세요.함수가 호출된 이유를 이해하려면 맨 아래에서 위쪽으로 호출 스택을 읽어보세요. 다음 예제는 할당을 수행하는 함수로 끝납니다.
<raw-address>, for example, 0x7f151 -> native code entry <interpreter trampoline> -> CPython dispatch <module> -> script top level EngineCoreProc.run_engine_core -> vLLM engine startup EngineCore.__init__ -> engine initialization EngineCore._initialize_kv_caches -> KV cache setup Worker.initialize_from_config -> worker setup GPUModelRunner.initialize_kv_cache -> model runner GPUModelRunner._allocate_kv_cache_tensors -> actual allocation
빠른 참조로 다음 표를 사용합니다.
| 목표 | 확인할 사항 |
|---|---|
| 가장 많은 메모리를 할당하는 항목을 찾습니다. | 스택 맨 위에 있는 가장 넓은 리프 막대 |
| 메모리를 가장 많이 차지하는 항목 찾기 | 스택 맨 아래에 있는 가장 넓은 막대 |
| 최적화 대상 식별 | 자동 할당 폭이 넓은 막대 |
| 건너뛸 수 있는 함수 식별 | 자체 할당이 0인 폭이 넓은 막대 |