Analisar gráficos de chama de criação de perfil de GPU no Serviço de Kubernetes do Azure (AKS) (versão prévia)

Gráficos de chama de perfil da GPU mostram quais funções consomem mais memória da GPU. Este artigo explica como interpretar os grafos de chama gerados pelas ferramentas de criação de perfil de GPU para AKS (Serviço de Kubernetes do Azure).

Important

As funcionalidades em versão preliminar do AKS estão disponíveis de forma optativa e por autoatendimento. As versões prévias são fornecidas “no estado em que se encontram” e “conforme disponíveis” e são excluídas dos contratos de nível de serviço e da garantia limitada. As versões prévias do AKS são parcialmente cobertas pelo suporte ao cliente em uma base de melhor esforço. Dessa forma, esses recursos não são destinados ao uso em produção. Para obter mais informações, consulte os seguintes artigos:

Antes de começar, configure a criação de perfil de GPU e confirme se os dados de criação de perfil estão disponíveis no Pyroscope e no Grafana.

Note

A primeira execução de criação de perfil pode não mostrar todos os símbolos, incluindo símbolos de operações de inicialização, como o carregamento de modelos, enquanto o criador de perfil analisa a carga de trabalho e resolve símbolos. Execute a carga de trabalho novamente após a análise inicial para capturar esses símbolos em perfis subsequentes.

Entender grafos de chama

Um gráfico de chama visualiza pilhas de chamadas com perfil. Cada barra representa uma função e as barras empilhadas mostram a cadeia de chamadas ou qual função chamou outra função. A largura de cada barra representa a quantidade do recurso medido, como tempo de CPU ou memória de GPU alocada, que flui por essa função.

Regra de chave: quanto maior a barra, mais o recurso medido flui por essa função.

Os seguintes gráficos de chama de exemplo foram capturados de uma carga de trabalho de inferência de vLLM.

Captura de tela da visualização inicial recolhida do gráfico de chama no Grafana para uma carga de trabalho do vLLM, mostrando barras empilhadas que representam pilhas de chamadas de alocação de memória da GPU.

Tip

Use Expandir todos os grupos no painel do gráfico de chama do Grafana para ver a pilha de chamadas completa sem recolhimento. Use a caixa Pesquisar para localizar funções ou palavras-chave específicas. Para evitar que o painel se recolha novamente, pause a atualização automática do painel definindo o intervalo de atualização como Desativado no canto superior direito do painel do Grafana enquanto você inspeciona o gráfico de chama expandido.

Captura de tela do gráfico de chama no Grafana para uma carga de trabalho do vLLM depois de selecionar Expandir todos os grupos, mostrando a pilha de chamadas completa com quadros de função individuais para alocações de memória da GPU.

Tip

Use o bloco de foco para focar em um caminho de alocação específico.

Ler os símbolos

Os rótulos de grafo de chama seguem estas convenções:

Formato de símbolo Meaning
Foo > bar class Foo: method def bar()
Foo > __init__ Construtor de classe Foo
bar (sozinho) Função independente def bar()
<interpreter trampoline> Sobrecarga de CPython. Você pode ignorar esse símbolo.
<raw-address>, por exemplo, 0x7f151 Código C nativo ou CUDA sem nenhum símbolo de Python disponível.

Os exemplos incluem:

  • GPUModelRunner / _allocate_kv_cache_tensors: um método em uma classe. Leia este símbolo como class GPUModelRunner: def _allocate_kv_cache_tensors(self).
  • LlamaMLP / __init__: um construtor chamado ao se criar um objeto LlamaMLP(...).
  • _compile_fx_inner: uma função autônoma no nível do módulo que não está dentro de uma classe.

Compreender a alocação própria em comparação com a alocação total

Use os valores Self e Total para determinar onde o recurso é consumido:

  • Total é o recurso consumido por uma função e tudo o que ele chama. Uma função pode ter um total elevado, mas não alocar nada por si só, porque faz parte da cadeia de chamadas.
  • Self é o recurso consumido diretamente pela função, excluindo suas funções filhas. Um alto auto-valor significa que essa função é onde o recurso é consumido.

Por exemplo, se GPUModelRunner._allocate_kv_cache_tensors tiver 55,1 GB de alocação própria, essa é a função que chama torch.empty() para criar os tensores de cache de chave-valor (KV).

Use as seguintes técnicas de navegação:

  • Comece pelos nós folha, que são barras que não têm nada acima. Toda a sua largura corresponde à autoalocação.
  • Ignore barras amplas com autoalocação zero ao pesquisar a origem de uma alocação. Essas funções orquestram as chamadas para outras funções.
  • Investigue barras amplas com alta autoalocação como possíveis alvos de otimização.

Localizar o maior consumidor de recursos

Use as seguintes etapas para identificar pontos de acesso:

  1. Examine as barras mais largas na parte superior do grafo. Essas funções folha alocam memória. Quanto maior a barra, mais memória ela consome.

  2. Verifique a alocação própria em relação à alocação total. Uma barra larga na parte inferior com self: 0 faz parte da cadeia de chamadas. Siga para cima até encontrar uma barra com alta alocação própria.

  3. Leia a pilha de chamadas de baixo para cima para entender por que a função foi chamada. O exemplo a seguir termina com a função que executa a alocação:

    <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
    

Use a tabela a seguir como uma referência rápida:

Goal O que procurar
Localizar o que aloca mais memória Barra de folha mais larga no topo da pilha
Localizar o que é responsável pela maior parte da memória Barra mais larga na parte inferior da pilha
Identificar destinos de otimização Barras com ampla autoalocação
Identificar funções que você pode ignorar Barras largas sem alocação própria

Próximas Etapas