Estrategias de Optimización de Rendimiento
La optimización del rendimiento en sistemas de inferencia o entrenamiento de modelos es un proceso sistemático que sigue un ciclo de "Análisis - Identificación - Mejora". Utilizando herramientas de perfilado, es posible pinpointar los cuellos de botella clave antes de aplicar intervenciones específicas.
Las métricas de rendimiento a nivel de operador, obtenidas mediante herramientas de profiling, son cruciales. Estas métricas suelen centrarse en dos aspectos principales: el tiempo de ejecución computacional del operador y el tiempo de comunicación/planificación de la tarea.
Cuando el tiempo de cálculo de un operador es excesivo, la solución a menudo implica mejoras intrínsecas del propio operador. En tales casos, se recomienda recopilar información sobre el shape y dtype del operador y reportar el problema al equipo de desarrollo para su seguimiento. Por otro lado, los procesos de planificación y despacho pueden ser complejos. Un problema común que afecta el rendimiento es la recompilación repetitiva de operadores. Esto se puede mitigar desactivando la compilación en línea, sustituyendo operadores por sus equivalentes aclnn más eficientes o aplicando técnicas de fusión de operadores.
Recopilación de Datos de Rendimiento
La herramienta de profiling de PaddlePaddle permite capturar los tiempos de cálculo y despacho de cada operador en un modelo. A continuación, se detallan los pasos para configurar y utilizar esta funcionalidad.
Importación del Módulo de Perfilado
Primero, importe el módulo paddle.profiler en su código Python. Para evitar conflictos de nombres si ya utiliza una variable llamada profiler, se recomienda usar un alias:
import paddle.profiler as pp_profiler
Inicialización del Perfilador
Configure la instancia del perfilador especificando los objetivos de recopilación de datos. Para dispositivos personalizados como la NPU, se deben definir targets y custom_device_types:
perfilador_instancia = pp_profiler.Profiler(
targets=[pp_profiler.ProfilerTarget.CUSTOM_DEVICE],
custom_device_types=['npu']
)
targets: Debe establecerse enpp_profiler.ProfilerTarget.CUSTOM_DEVICE.custom_device_types: Debe configurarse como'npu'.
Selección del Momento para la Recopilación de Datos
Es fundamental recolectar los datos de rendimiento durante una fase estable del entrenamiento para obtener mediciones representativas. Considere las siguientes directrices:
- Evite los primeros 100 pasos (steps) del entrenamiento, ya que suelen corresponder a fases de inicialización y no reflejan el rendimiento sostenido. Es preferible iniciar la recolección después del paso 100.
- Si un epoch contiene menos de 100 pasos, realice la recopilación en los pasos finales del epoch, una vez que el rendimiento se haya estabilizado.
Ejemplo de Código para la Recolección
Integre la lógica de inicio y detención del perfilador en su script de entrenamiento, por ejemplo, en train.py:
# ... dentro de su bucle de entrenamiento ...
iteracion_actual = obtener_iteracion_actual() # Función para obtener el número de iteración
if iteracion_actual == 100:
perfilador_instancia.start()
elif iteracion_actual == 101:
perfilador_instancia.stop()
# ...
Una vez que haya insertado este fragmento de código, proceda a ejecutar su proceso de entrenamiento.
Aálisis de los Datos de Rendimiento
Interpretación de Datos con msprof
Al finalizar el entrenamiento, se creará un directorio llamado ascend_profiling en su ruta de trabajo actual. Este contiene los datos de rendimiento brutos. Para analizar estos datos, utilice la herramienta msprof, incluida en el paquete CANN. Los comandos típicos son:
# Configurar un alias para msprof (ajuste la ruta según su instalación de CANN)
alias msprof='/usr/local/Ascend/ascend-toolkit/latest/tools/profiler/bin/msprof'
# Exportar los datos de perfilado
msprof --export=on --output=ascend_profiling
Nota: La ruta /usr/local/Ascend es la ubicación de instalación predeterminada de CANN. Si ha instalado el paquete en una ruta personalizada, deberá ajustar el alias en consecuencia.
Una vez que la operación de análisis se complete con éxito, el proceso generará un conjunto de directorios, uno por cada tarjeta (NPU) utilizada, conteniendo los resultados. Dentro de cada resultado, preste especial atención a la carpeta mindstudio_profiler_output. Los archivos clave en esta carpeta son:
msprof_*.json: Archivo de línea de tiempo que puede ser visualizado enchrome://tracingpara una representación gráfica clara del flujo de ejecución.op_statistic_*.csv: Informe estadístico de los operadores.op_summary_*.csv: Resumen cronológico de la ejecución de operadores.
Análisis del archivo op_statistic_*.csv
Este archivo proporciona estadísticas de rendimiento de los operadores en la NPU y contiene los siguientes campos clave:
- OP Type: Identificador único del tipo de operador.
- Ratio (%): Porcentaje del tiempo total de cómputo consumido por este tipo de operador (un indicador clave de cuello de botella).
- Core Type: Tipo de unidad de cómputo de la NPU que ejecutó el operador.
- Total Time (us): Tiempo total de cómputo del operador en microsegundos.
Campo OP Type
El campo OP Type registra los nombres específicos de todos los operadores que se ejecutan durante el análisis de rendimiento. Permite identificar rápidamente el comportamiento de un operador en particular.
Campo Ratio (%)
Ratio (%) indica la proporción del tiempo total de ejecución que un operador consume. Generalmente, un valor inferior al 40% no es una preocupación significativa. Sin embargo, un porcentaje muy elevado podría señalar un problema de recompilación repetitiva del operador. Para mitigar esto, se puede establecer la variable de entorno export FLAGS_npu_jit_compile=false.
Por ejemplo, si un operador TransData, a pesar de ser invocado con frecuencia, mantiene un porcentaje de Ratio aceptable. Al desactivar la compilación JIT, el Ratio (%) de TransData podría reducirse de un 19.637% a un 18.176%, indicando una mejora.
Campo Total Time (us)
Total Time (us) representa el tiempo total que el operador dedicó a la computación. Un tiempo total excesivamente alto, o una gran disparidad entre el tiempo máximo y el promedio de ejecución del operador, podría sugerir problemas en su invocación o implementación.
Campo Core Type
El campo Core Type especifica la unidad de cómputo de la NPU responsable de ejecutar el operador. Se clasifican principalmente en:
- AI_CORE: La unidad de cómputo de alto rendimiento de la NPU, optimizada para operaciones intensivas como convoluciones y multiplicaciones de matrices. Maximizar la utilización de
AI_COREes fundamental para mejorar el rendimiento global. - AI_CPU: Una unidad de cómputo de propósito general en la NPU que soporta diversos tipos de datos. Se encarga de operaciones más ligeras como cómputos escalares y control de flujo. Ofrece flexibilidad, pero su rendimiento computacional es inferior al de
AI_CORE. - AI_VECTOR_CORE: Una subunidad de
AI_COREdedicada a cálculos vectoriales. Su impacto directo en la optimización general suele ser menor y no requiere atención especial a menos que se identifique un cuello de botella específico. - MIX_AIV: Indica un modo de ejecución híbrido, donde parte del cómputo se realiza en
AI_COREy otra parte enAI_CPU. No suele ser una prioridad de optimización si su contribución al tiempo total no es desproporcionadamente alta.
Análisis del archivo op_summary_*.csv
El archivo op_summary_*.csv registra datos de secuencia de ejecución de los operadores durante el período de perfilado. Incluye los siguientes campos cruciales:
- Op Name: Nombre de la instancia específica del operador en ejecución.
- OP Type: Identificador del tipo de operador (corresponde al campo
OP Typeenop_statistic_*.csv). - Task Duration (us): Tiempo de cómputo real del operador en microsegundos.
- Task Wait Time (us): Tiempo de despacho o espera del operador en microsegundos.
Campo Op Name
El campo Op Name identifica cada instancia de operador ejecutada. Puede aparecer con un prefijo aclnn o sin él, lo que distingue dos métodos de invocación de operadores en CANN:
- Los operadores aclop ofrecen una alta compatibilidad, cubriendo casi todos los operadores. Sin embargo, su rendimiento es inferior al de
aclnn. Un problema significativo conaclopes que los cambios en los atributos pueden llevar a recompilaciones repetidas, lo que consume rendimiento. Si se observa una recompilación frecuente, se recomienda migrar a operadoresaclnnpara reducir el tiempo de cálculo. - Los operadores aclnn suelen ofrecer un mejor rendimiento.
Campo Task Duration (us)
Task Duration (us) representa el tiempo de cálculo del operador en el lado de la NPU. Al ordenar este campo en orden descendente en el archivo op_summary_*.csv, se pueden identificar rápidamente los principales cuellos de botella de cómputo.
Si el Task Duration (us) de un operador es excesivamente largo (por ejemplo, más de 1000 µs), es crucial investigarlo. Esto podría indicar un problema en la invocación del operador dentro del modelo o en su lógica de capa de adaptación.
Campo Task Wait Time (us)
Task Wait Time (us) mide el tiempo de despacho o espera del operador, es decir, el tiempo que tarda el operador en ser enviado desde la CPU a la NPU.
Este tiempo es a menudo más susceptible de optimización a nivel de la capa de adaptación. Al analizar Task Wait Time (us) en orden descendente, se suelen identificar tres causas principales para tiempos de espera prolongados:
- Espera intermedia (forward/backward): Generalmente, es un tiempo de espera normal y esperado entre operaciones.
- Recompilación repetitiva de operadores: Esta es la causa más común de un aumento significativo en el
Task Wait Time (us)y representa una importante oportunidad de optimización. - Ejecución del operador anterior en la CPU: Aunque no se registra directamente en los datos de rendiimento como un tiempo de espera, si un operador previo se ejecuta en la CPU, puede provocar un aumento sustancial en el tiempo de espera del operador actual en la NPU.
Las dos últimas situaciones son los principales focos de optimización. Es necesario localizar la fuente del tiempo de espera prolongado y luego revisar las relaciones de invocación en el código para aplicar mejoras específicas.