Analizar gráficos de llama de perfilado de GPU en Azure Kubernetes Service (AKS) (versión preliminar)

Los gráficos de llama de perfilado de GPU muestran qué funciones consumen más memoria de GPU. En este artículo se explica cómo interpretar los gráficos de llama generados por las herramientas de generación de perfiles de GPU para Azure Kubernetes Service (AKS).

Important

Las características en versión preliminar de AKS están disponibles en modalidad de autoservicio y previa suscripción. Las versiones preliminares se proporcionan "tal cual" y "como están disponibles", y están excluidas de los Acuerdos de nivel de servicio y la garantía limitada. Las versiones preliminares de AKS cuentan con soporte parcial por parte del servicio al cliente en la medida de lo posible. Por lo tanto, estas características no están diseñadas para su uso en producción. Para más información, consulte los siguientes artículos de soporte:

Antes de empezar, configure la generación de perfiles de GPU y confirme que los datos de generación de perfiles están disponibles en Pyroscope y Grafana.

Note

Es posible que la primera ejecución de generación de perfiles no muestre todos los símbolos, incluidos los símbolos de las operaciones de inicio, como la carga de modelos, mientras que el generador de perfiles analiza la carga de trabajo y resuelve los símbolos. Vuelva a ejecutar la carga de trabajo después del análisis inicial para capturar estos símbolos en perfiles posteriores.

Comprender los gráficos de llama

Un gráfico de llama muestra pilas de llamadas perfiladas. Cada barra representa una función y las barras apiladas muestran la cadena de llamadas o qué función llamó a otra función. El ancho de cada barra representa la cantidad del recurso medido, como el tiempo de CPU o la memoria de GPU asignada, que fluye a través de esa función.

Regla clave: cuanto más ancha sea una barra, mayor será la cantidad del recurso medido que fluye a través de esa función.

Los siguientes gráficos de llama de ejemplo se han obtenido a partir de una carga de trabajo de inferencia de vLLM.

Captura de pantalla de la vista inicial colapsada del gráfico de flamas en Grafana para una carga de trabajo de vLLM, que muestra barras apiladas que representan pilas de llamadas de asignación de memoria de la GPU.

Tip

Use Expandir todos los grupos en el panel del gráfico de llamas de Grafana para ver la pila de llamadas completa sin contraerse. Use el cuadro Buscar para buscar funciones o palabras clave específicas. Para evitar que el panel vuelva a contraerse, detén la actualización automática del panel de control estableciendo el intervalo de actualización en Desactivado en la esquina superior derecha del panel de control de Grafana mientras examinas el gráfico de llama expandido.

Captura de pantalla del diagrama de llamas en Grafana para una carga de trabajo de vLLM después de seleccionar Expandir todos los grupos, que muestra la pila de llamadas completa con bloques de funciones individuales correspondientes a asignaciones de memoria de la GPU.

Tip

Usa Focus block para centrarte en una ruta de asignación específica.

Leer los símbolos

Las etiquetas de gráfico de llama siguen estas convenciones:

Formato de símbolo Meaning
Foo > bar class Foo: method def bar()
Foo > __init__ Constructor de clase Foo
bar (solo) Función independiente def bar()
<interpreter trampoline> Sobrecarga de CPython. Puede omitir este símbolo.
<raw-address>, por ejemplo, 0x7f151 Código nativo de C o CUDA sin ningún símbolo de Python disponible.

Algunos ejemplos son:

  • GPUModelRunner / _allocate_kv_cache_tensors: un método en una clase. Lea este símbolo como class GPUModelRunner: def _allocate_kv_cache_tensors(self).
  • LlamaMLP / __init__: Un constructor que se llama al crear un objeto LlamaMLP(...).
  • _compile_fx_inner: función de nivel de módulo independiente que no está dentro de una clase.

Comprender la asignación propia frente a la asignación total

Use los valores Self y Total para determinar dónde se consume el recurso:

  • Total es el recurso consumido por una función y todo lo que esta invoca. Una función puede tener un total elevado pero no asignar nada por sí misma porque forma parte de la cadena de llamadas.
  • Self es el recurso consumido directamente por la función, excluyendo sus funciones secundarias. Un valor self alto indica que en esta función se consume el recurso.

Por ejemplo, si GPUModelRunner._allocate_kv_cache_tensors tiene 55,1 GB de autoasignación, se trata de la función que invoca a torch.empty() para crear los tensores de la caché de clave-valor (KV).

Use las siguientes técnicas de navegación:

  • Empiece por los nodos hoja, que son barras que no tienen nada encima. Todo su ancho representa la autoasignación.
  • Omite las barras anchas con autoasignación nula cuando busques el origen de una asignación. Estas funciones orquestan llamadas a otras funciones.
  • Investigue las barras anchas con una alta autoasignación como posibles objetivos de optimización.

Encuentra el recurso que más consume

Siga estos pasos para identificar zonas activas:

  1. Examine las barras más anchas en la parte superior del gráfico. Estas funciones hoja asignan memoria. Cuanto más ancho sea la barra, más memoria consume.

  2. Compare la asignación propia con la asignación total. Una barra ancha en la parte inferior con self: 0 forma parte de la cadena de llamadas. Sígala hacia arriba hasta que encontrar una barra con una autoasignación alta.

  3. Lea la pila de llamadas de abajo a arriba para comprender por qué se llamó a la función. En el ejemplo siguiente se termina con la función que realiza la asignación:

    <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 la tabla siguiente como referencia rápida:

Goal Qué se debe tener presente
Descubra qué es lo que más memoria asigna La barra de hoja más ancha en la parte superior de la pila
Descubre qué consume más memoria La barra más ancha en la parte inferior de la pila
Identificación de los destinos de optimización Barras con una amplia autoasignación
Identificar funciones que puede omitir Barras anchas con autoasignación nula

Pasos siguientes