Analisar gráficos de chama de criação de perfis de GPU no Azure Kubernetes Service (AKS) (Pré-visualização)

Os gráficos de chama de perfilagem da GPU mostram quais as funções que consomem mais memória da GPU. Este artigo explica como interpretar os grafos de chama gerados pelas ferramentas de perfilagem de GPU para o Azure Kubernetes Service (AKS).

Important

As funcionalidades de pré-visualização do AKS estão disponíveis num regime de autosserviço e de adesão voluntária. As visualizações prévias são fornecidas "como estão" e "conforme disponíveis" e são excluídas dos contratos de nível de serviço e da garantia limitada. As versões de pré-visualização do AKS são apenas parcialmente abrangidas pelo suporte ao cliente, na medida do possível. Assim sendo, estas funcionalidades não se destinam ao uso em produção. Para obter mais informações, consulte os seguintes artigos de suporte:

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

Note

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

Compreenda os grafos de chama

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

Regra chave: quanto maior a barra, mais recursos medidos passam por essa função.

Os seguintes gráficos de chama de exemplo são capturados a partir de uma carga de trabalho de inferência vLLM.

Captura de ecrã da vista inicial do grafo de chama colapsada no Grafana para uma carga de trabalho vLLM, mostrando barras empilhadas que representam as 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 completa de chamadas sem colapsar. Use a caixa de Pesquisa para encontrar funções ou palavras-chave específicas. Para evitar que o painel volte a colapsar, coloque em pausa a atualização automática do dashboard definindo o intervalo de atualização para Desativado no canto superior direito do dashboard do Grafana, enquanto inspeciona o gráfico de chama expandido.

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

Tip

Utilize Focus block para se concentrar num caminho de alocação específico.

Leia os símbolos

As etiquetas de grafos de chama seguem estas convenções:

Formato do símbolo Meaning
Foo > bar class Foo: method def bar()
Foo > __init__ Construtor da classe Foo
bar (sozinho) Função independente def bar()
<interpreter trampoline> CPython por cima. Podes ignorar este símbolo.
<raw-address>, por exemplo, 0x7f151 Código nativo C ou CUDA sem símbolo Python disponível.

Os exemplos incluem:

  • GPUModelRunner / _allocate_kv_cache_tensors: Um método numa classe. Leia este símbolo como class GPUModelRunner: def _allocate_kv_cache_tensors(self).
  • LlamaMLP / __init__: Um construtor invocado quando se cria um objeto LlamaMLP(...).
  • _compile_fx_inner: Uma função autónoma ao 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

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

  • Total é o recurso consumido por uma função e tudo o que ela chama. Uma função pode ter um total elevado mas não alocar nada porque faz parte da cadeia de chamadas.
  • Self são os recursos consumidos diretamente pela função, excluindo as suas funções subordinadas. Um valor próprio elevado significa que é nesta função que o recurso é consumido.

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

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

  • Começa com os nós folha, que são barras sem nada acima deles. A largura total representa a autoatribuição.
  • Evita barras largas sem alocação própria quando procuras a origem de uma alocação. Estas funções orquestram chamadas para outras funções.
  • Investigue barras largas com alta auto-alocação como potenciais alvos de otimização.

Encontre o maior consumidor de recursos

Use os seguintes passos para identificar pontos críticos:

  1. Olha para as barras mais largas no topo do gráfico. Estas funções folha alocam memória. Quanto mais larga for a barra, mais memória consome.

  2. Verifique a alocação própria face à alocação total. Uma barra larga na parte inferior com self: 0 faz parte da cadeia de chamadas. Segue-a para cima até encontrares uma barra com elevada autoalocação.

  3. Lê a pilha de chamadas de baixo para cima para perceber porque é que a função foi chamada. O exemplo seguinte 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 seguinte como referência rápida:

Goal O que procurar
Encontre o que aloca mais memória Barra de folhas mais larga no topo da pilha
Encontrar o que é responsável pela maior quantidade de memória Barra mais larga na base da pilha
Identificar alvos de otimização Barras com ampla auto-alocação
Identifica funções que podes saltar Barras largas com zero autoalocação

Passos seguintes