Ayude a mejorar esta página
Para contribuir a esta guía del usuario, elija el enlace Edit this page on GitHub que se encuentra en el panel derecho de cada página.
Aceleración de la carga de modelos en Amazon EKS
Al implementar modelos de lenguaje de gran tamaño (LLM) en Amazon EKS, el tiempo de carga de los modelos afecta directamente a la rapidez con la que los pods pueden comenzar a atender las solicitudes de inferencia. Esto es especialmente cierto durante los eventos de escalado vertical, cuando los nuevos pods o nodos deben cargar el modelo antes de gestionar el tráfico. El inicio del modelo consta de dos fases que puede mejorar al ajustar y almacenar en caché los artefactos de compilación:
-
Carga de pesos: transmisión de archivos de pesos del modelo desde Amazon S3 a la memoria de la GPU mediante Run:ai Model Streamer
. -
torch.compile: compilación del gráfico de computación del modelo en kernels de CUDA o Triton fusionados optimizados. Esta compilación se ejecuta en el primer inicio y puede afectar significativamente al tiempo de inicio en función del tamaño del modelo.
En este tema, se muestra cómo optimizar el rendimiento de Run:ai Model Streamer y el almacenamiento en caché de torch.compile para reducir ambas fases del proceso de carga del modelo. Para ver el procedimiento completo de implementación de vLLM en Amazon EKS para inferencia, consulte Modelos de carga y servicio en Amazon EKS.
Optimización de la ruta de red de S3 en el modo automático de EKS
Si utiliza el modo automático de EKS con nodos de GPU en subredes privadas, le recomendamos que utilice un punto de conexión de VPC de puerta de enlace para S3 a fin de optimizar la ruta de red entre los nodos y S3. Con el punto de conexión de VPC de puerta de enlace, el tráfico a S3 permanece en la red de AWS y evita por completo la puerta de enlace de NAT, por lo que no hay un límite de ancho de banda compartido ni ningún cargo por procesamiento de datos de NAT por GB. Sin el punto de conexión de VPC de puerta de enlace, cuando el tráfico atraviesa una puerta de enlace de NAT, esta se convierte en un cuello de botella compartido durante el escalado vertical, cuando varios nodos extraen el modelo al mismo tiempo.
El modo automático de EKS suele colocar los nodos en subredes privadas, por lo que el tráfico a S3 fluye a través de una puerta de enlace de NAT de forma predeterminada. Una puerta de enlace de NAT proporciona hasta 100 Gbps de ancho de banda y 55 000 conexiones simultáneas por destino. Sin embargo, ese ancho de banda se comparte entre todos los nodos de la subred privada. Durante un evento de escalado vertical, varios nodos que descargan el modelo completo a la vez compiten por el mismo ancho de banda de la puerta de enlace de NAT, lo que puede ralentizar la carga de los pesos del modelo en todos ellos.
Ajuste del rendimiento de Run:ai Model Streamer
Los motores de inferencia como vLLM y SGLang utilizan Run:ai Model Streamer como mecanismo alternativo para cargar los pesos durante el inicio de la inferencia.
De forma predeterminada, Run:ai Model Streamer utiliza una configuración de simultaneidad conservadora al descargar los archivos de peso del modelo desde S3. Al aumentar la simultaneidad de descarga y el tamaño de los fragmentos, se reduce el tiempo de carga de los pesos del modelo al descargar más datos en paralelo.
Cálculo de la simultaneidad óptima
Calcule el valor de simultaneidad óptimo de la siguiente manera:
concurrency = ceil(total_model_size_gb / chunk_size_gb)
Sustituya el valor concurrency que utiliza en función del tamaño del modelo y del fragmento. Consulte la siguiente tabla para ver algunos ejemplos. Por ejemplo, con un modelo de 67 GB y un tamaño de fragmento de 4 GB: ceil(67 / 4) = 17.
| Tamaño del modelo | Tamaño del fragmento | Simultaneidad |
|---|---|---|
|
10 GB |
4 GB |
3 |
|
67 GB |
4 GB |
17 |
|
140 GB |
4 GB |
35 |
Aplicación de la configuración
Agregue los siguientes argumentos y variables de entorno a la especificación del contenedor de inferencia:
-
--tensor-parallel-size: el grado de paralelismo de tensores (TP) suele ser la cantidad mínima de GPU necesarias para que quepa el modelo en la memoria de la GPU en función del tamaño del modelo. Por ejemplo, un modelo de 67 GB en un tipo de instanciap5.48xlargerequiere al menos 2 GPU, así que debe establecerlo en2. -
concurrencyydistributed(en--model-loader-extra-config): establezcaconcurrencyen el valor calculado para el modelo. Establezcadistributedentruesolo cuando se utilice el paralelismo de tensores (TP > 1). Cuando está habilitado, cada clasificación de paralelismo de tensores transmite su propia partición de peso directamente desde Amazon S3, en lugar de cargar todos los pesos en la clasificación 0 y transmitirlos a las demás clasificaciones. Esto mejora considerablemente el rendimiento de carga en las implementaciones de varias GPU. Déjelo sin establecer (ofalse) si TP = 1, donde no proporciona ningún beneficio. Esta opción requiere la arquitectura vLLM V1. Es incompatible con--enforce-eager, lo que fuerza la ruta V0; si se utilizan ambas juntas, se producirá un error o se regresará silenciosamente a una carga no distribuida. -
RUNAI_STREAMER_CHUNK_BYTESIZE: tamaño de fragmento de 4 GB. Este valor muestra de forma coherente el mejor rendimiento en todos los puntos de referencia. Los fragmentos más grandes reducen el número de solicitudes de S3 y mejoran el rendimiento en las instancias con un gran ancho de banda. -
RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS: tiempo de espera por solicitud en milisegundos. Permite un reintento más rápido en respuestas lentas de S3. -
RUNAI_STREAMER_S3_LOW_SPEED_LIMIT: velocidad de transferencia mínima en bytes por segundo antes de que una solicitud se considere lenta y se vuelva a intentar.
containers: - name: vllm-inference image: vllm/vllm-openai:v0.21.0 command: - python3 - -m - vllm.entrypoints.openai.api_server args: # ... your existing args ... - --model=s3://<MODEL_PATH> - --tensor-parallel-size=2- --load-format=runai_streamer - --model-loader-extra-config={"concurrency":17,"distributed":true} env: - name: RUNAI_STREAMER_CHUNK_BYTESIZE value:"4294967296"- name: RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS value:"3000"- name: RUNAI_STREAMER_S3_LOW_SPEED_LIMIT value:"1048576"
Optimización del arranque en frío de torch.compile
El paso torch.compile del proceso de procesamiento de inferencias traza el gráfico de computación de un modelo (la secuencia de operaciones matemáticas) y lo compila en kernels de CUDA o Triton fusionados optimizados. No compila los pesos del modelo, solo las operaciones que los transforman.
Los motores de inferencia utilizan torch.compile porque proporciona mejoras significativas en el rendimiento de forma automática, sin necesidad de una ingeniería de kernels personalizada para cada arquitectura de modelo:
-
Fusión de kernels: varias operaciones pequeñas (adición residual, layernorm, activación) se fusionan en un solo kernel, lo que reduce los viajes de ida y vuelta de la memoria de la GPU.
-
Menos lanzamientos de kernels: una capa de transformador pasa de entre 15 y 30 kernels de CUDA independientes a unos 10 fusionados, lo que reduce la sobrecarga de la CPU por lanzamiento.
-
Eliminación de Python de la ruta crítica: toda la fase de avance se convierte en un plan de ejecución de C++, lo que elimina la sobrecarga del intérprete de Python entre las operaciones.
-
Mejor compatibilidad con los gráficos de CUDA: los gráficos estáticos compilados se capturan y reproducen con una sobrecarga de la CPU prácticamente nula.
-
Optimización automática: funciona con cualquier arquitectura de modelo. El rendimiento mejora entre un 5 % y un 30 % en comparación con el modo Eager.
En la siguiente tabla, se muestran los motores de inferencia más comunes que utilizan torch.compile.
Problema de arranque en frío de torch.compile
La desventaja de torch.compile es que el primer pod de inferencia se debe compilar antes de poder atender las solicitudes. Esta compilación puede tardar varios minutos, en función del tamaño del modelo. Los artefactos compilados son pequeños (aproximadamente 15 MB para un modelo de 60 GB), pero aumentan considerablemente el tiempo de arranque en frío. Como los artefactos son pequeños y deterministas para una configuración determinada, puede almacenarlos en caché y reutilizarlos para evitar la penalización de arranque en frío al iniciar el pod y el nodo posteriormente.
Los artefactos constan de:
-
Archivos de origen del kernel de Triton generados
-
Archivos binarios del kernel compilados (
.cubin) -
Estructura de gráficos que secuencia las llamadas del kernel
La compensación de --enforce-eager
vLLM habilita torch.compile y la captura de gráficos de CUDA de forma predeterminada. El indicador --enforce-eager los desactiva ambos y ejecuta el modelo en modo Eager, donde cada operación se ejecuta inmediatamente a través del intérprete de Python. Como el modo Eager omite tanto la compilación como la captura de gráficos, algunas guías de inicio rápido (incluida la implementación básica en Modelos de carga y servicio en Amazon EKS) utilizan --enforce-eager para iniciar los pods más rápido.
--enforce-eager es una opción válida para la depuración, las implementaciones con limitaciones de memoria o las arquitecturas de modelo que no se compilan de forma limpia, y puede usarlo en producción por esos motivos. Si bien puede mitigar la penalización por arranque en frío de torch.compile, recomendamos otros enfoques, como los que se detallan en las siguientes secciones de esta página, para mantener el rendimiento en tiempo de ejecución en producción.
Comprenda la relación entre el tiempo de inicio y el rendimiento en tiempo de ejecución de --enforce-eager antes de la implementación en producción.
| Aspecto | Con --enforce-eager (modo Eager) |
Predeterminado (torch.compile + gráficos de CUDA) |
|---|---|---|
|
Tiempo de inicio |
Rápido: sin pasos de compilación ni captura de gráficos |
Arranque en frío lento (el problema que resuelven Almacenamiento de artefactos de torch.compile en el mismo nodo y Precalentamiento de la caché de torch.compile en nodos nuevos) |
|
Rendimiento en estado estable |
Referencia |
Entre un 5 % y un 30 % más gracias a la fusión de kernels |
|
Latencia por token (lote pequeño) |
Mayor sobrecarga de lanzamiento de la CPU |
Mucho más bajo: los gráficos de CUDA reproducen los lanzamientos del kernel como una sola unidad |
|
Memoria de GPU |
Más bajo y más predecible |
Más alto: la captura de gráficos preasigna los grupos de búferes |
|
Depurabilidad |
Seguimientos de pila limpios por operación |
Los errores aparecen dentro de los kernels generados |
Almacenamiento de artefactos de torch.compile en el mismo nodo
Esta técnica se aplica cuando se utiliza un motor de inferencia compatible con torch.compile, como vLLM (activado de forma predeterminada) o SGLang (activado mediante --enable-torch-compile). Solo funciona cuando torch.compile está activo, es decir, cuando --enforce-eager no se ha configurado.
importante
Esta mejora no tiene ningún efecto si torch.compile está deshabilitado. En vLLM, el indicador --enforce-eager desactiva torch.compile por completo, por lo que no se compila ni almacena en caché ningún artefacto. Si ha seguido la implementación base en Modelos de carga y servicio en Amazon EKS con --enforce-eager, vLLM crea el directorio de caché, pero nunca escribe en él. Elimine --enforce-eager antes de aplicar esta técnica.
Cuando un motor de inferencia compila el gráfico de computación del modelo en el primer inicio, puede almacenar en caché los kernels optimizados resultantes en el almacenamiento local del nodo. Los pods posteriores del mismo nodo reutilizan los artefactos almacenados en caché y omiten por completo el paso de compilación, lo que puede reducir considerablemente el tiempo de inicio.
Adición de variables de entorno de caché
Agregue las siguientes variables de entorno a la especificación del contenedor de inferencia para dirigir la caché de torch.compile y Triton a una ruta de host persistente. Recomendamos utilizar el almacén de instancias de NVMe local del nodo y no el volumen raíz de Amazon Elastic Block Store (Amazon EBS) para hostPath. Para obtener ejemplos, consulte la siguiente sección.
containers: - name: vllm-inference env: # torch.compile cache - name: XDG_CACHE_HOME value:"/compile-cache"- name: TORCHINDUCTOR_CACHE_DIR value:"/compile-cache/inductor"- name: TRITON_CACHE_DIR value:"/compile-cache/triton"volumeMounts: - name: compile-cache mountPath: /compile-cache volumes: - name: compile-cache hostPath: path:/mnt/k8s-disks/0/compile-cachetype: DirectoryOrCreate
Establecimiento de la ruta de la caché al almacén de instancias de NVMe
Los artefactos compilados y los pesos transmitidos se benefician de un almacenamiento local rápido. En las instancias de GPU con almacén de instancias de NVMe (como las instancias de las familias G y P), el almacén de instancias ofrece aproximadamente 30 GB/s, en comparación con aproximadamente 1 GB/s del volumen raíz de Amazon EBS. Dirija hostPath de la caché al punto de montaje de NVMe para optimizar el rendimiento.
importante
El volumen hostPath utiliza type: DirectoryOrCreate. Si dirige a una ruta que no está respaldada por el almacén de instancias de NVMe, Kubernetes crea el directorio de forma silenciosa en el volumen raíz de Amazon EBS. La caché continúa funcionando, pero se pierde el beneficio de rendimiento de NVMe sin errores ni advertencias.
El punto de montaje de NVMe y la forma de habilitarlo difieren entre el modo automático de EKS y los nodos autoadministrados:
| Computación | Punto de montaje de NVMe | Cómo habilitar el almacén de instancias de NVMe |
|---|---|---|
|
Modo automático de EKS |
|
Se habilita de forma dinámica en función del almacenamiento efímero solicitado. El modo automático de EKS formatea y monta el almacén de instancias de NVMe como una matriz RAID 0 cuando la instancia tiene varias unidades de NVMe, solo cuando el |
|
Karpenter autoadministrado |
|
Establezca |
Para el modo automático de EKS, establezca hostPath en /mnt/.ephemeral/compile-cache en las especificaciones del contenedor:
volumes: - name: compile-cache hostPath: path: /mnt/.ephemeral/compile-cache type: DirectoryOrCreate
Para Karpenter autoadministrado, establezca hostPath en /mnt/k8s-disks/0/compile-cache en las especificaciones del contenedor:
volumes: - name: compile-cache hostPath: path: /mnt/k8s-disks/0/compile-cache type: DirectoryOrCreate
Resultados de ejemplo
-
El primer pod de un nodo ejecuta torch.compile y escribe los kernels compilados en
/compile-cacheen el host. -
Los pods posteriores del mismo nodo montan la caché existente y omiten la compilación por completo, lo que reduce el tiempo de arranque en frío de torch.compile de aproximadamente entre 50 y 80 s a aproximadamente entre 4 y 6 s.
En la siguiente tabla, se muestra la mejora del almacenamiento en caché en el mismo nodo para modelos de diferentes tamaños:
| Tamaño del modelo | Primer pod de torch.compile (sin caché) | Pods posteriores de torch.compile (mismo nodo) |
|---|---|---|
|
60 GB |
Aproximadamente 53 s |
Aproximadamente 6 s |
|
140 GB |
Aproximadamente 60 s |
Aproximadamente 6 s |
|
640 GB |
Aproximadamente 80 s |
Aproximadamente 6 s |
Precalentamiento de la caché de torch.compile en nodos nuevos
Esta técnica se aplica cuando se ejecuta una inferencia de varios nodos con GPU homogéneas, paralelismo de tensores, modelos y versiones de PyTorch entre nodos. La técnica de Almacenamiento de artefactos de torch.compile en el mismo nodo se centra en el caso de un solo nodo, pero los nuevos nodos que se agregan durante los eventos de escalado vertical comienzan con una caché de torch.compile vacía. Para reducir aún más el tiempo de arranque en frío en los nodos recién inicializados, puede implementar un mecanismo de almacenamiento en caché que almacene los artefactos de torch.compile compilados en S3 y los predescargue en los nodos nuevos cuando se unan al clúster.
El enfoque general es el siguiente:
-
Cuando el primer pod compile el modelo en el primer nodo, cargue los artefactos de torch.compile (aproximadamente 15 MB) a un bucket de S3.
-
Cuando se unan nuevos nodos al clúster, descargue los artefactos en caché al almacenamiento local del nodo antes de programar los pods de inferencia.
Por ejemplo, puede implementar un DaemonSet que se ejecute en nodos de GPU y que empaquete y cargue la caché de torch.compile en S3. También puede sincronizar esa caché con el almacenamiento local del nodo antes de programar los pods de inferencia en los nodos nuevos.
Consideraciones sobre el almacenamiento en caché entre nodos
Al almacenar en caché los artefactos de torch.compile entre nodos, los kernels compilados solo son válidos cuando estos parámetros coinciden entre el nodo que generó la caché y el nodo que la consume. El mecanismo de almacenamiento en caché debe tener en cuenta todos estos aspectos. Una discrepancia en algún parámetro produce una caché no válida que fuerza la recompilación o provoca errores en tiempo de ejecución. La herramienta de almacenamiento en caché debe diferenciar los artefactos según estos parámetros, por ejemplo, al incorporarlos a la clave de objeto de S3 o a la estructura de directorios de caché.
| Parámetro | ¿Por qué importa? |
|---|---|
|
Tipo de GPU |
Los kernels compilados son específicos de la arquitectura de la GPU (por ejemplo, sm_90 para H100 frente a sm_89 para L4). |
|
Paralelismo de tensores (TP) |
Los diferentes grados de TP producen diferentes particiones de gráficos de computación. |
|
Modelo |
La arquitectura y el tamaño de cada modelo se compilan en diferentes kernels. |
|
Versión PyTorch |
Los componentes internos del compilador de Triton y torch.compile pueden introducir cambios bruscos entre las versiones. |
Resultados de ejemplo
En las pruebas direccionales con el precalentamiento de la caché entre nodos (Qwen3-6-35B-A3B, 67 GB, 2 GPU con TP = 2 en p5.48xlarge), el primer pod en nodos recién escalados alcanzó el mismo tiempo de inicio que los pods posteriores en un nodo ya caliente:
| Escenario | Primer pod | Segundo pod |
|---|---|---|
|
Sin almacenamiento en caché entre nodos (nuevo nodo) |
65 s |
16 s |
|
Con almacenamiento en caché entre nodos (nuevo nodo) |
16 s |
16 s |
Ejemplo de implementación
En el siguiente ejemplo, se combina el ajuste de rendimiento de Run:ai Model Streamer y una caché de torch.compile en un solo manifiesto de implementación de vLLM. Reemplace los valores de marcador de posición por su propia configuración:
-
serviceAccountName: cuenta de servicio con un rol de IAM que tiene acceso de lectura de Amazon S3 al bucket del modelo. -
nodeSelector(karpenter.sh/nodepool): nombre del grupo de nodos de la GPU (por ejemplo,gpu-nodepool-g6e-12xlarge). -
--model: ruta de Amazon S3 a los pesos del modelo. -
--model-loader-extra-config: establezcaconcurrencyen función del tamaño del modelo:ceil(total_model_size_gb / chunk_size_gb). Por ejemplo, un modelo de 67 GB con un tamaño de fragmento de 4 GB da como resultadoceil(67 / 4) = 17. -
--tensor-parallel-size: establezca el grado de paralelismo de tensores (TP) a la cantidad mínima de GPU necesarias para que quepa el modelo en la memoria. -
pathhostPath: punto de montaje del almacén de instancias de NVMe para nodos autoadministrados con Karpenter. En el modo automático de EKS, use/mnt/.ephemeral/compile-cacheen su lugar. Para obtener más información, consulte Almacenamiento de artefactos de torch.compile en el mismo nodo.
apiVersion: apps/v1 kind: Deployment metadata: name: vllm-inference namespace: default spec: replicas: 1 selector: matchLabels: app: vllm-inference template: metadata: labels: app: vllm-inference spec: serviceAccountName: <SERVICE_ACCOUNT_NAME> nodeSelector: karpenter.sh/nodepool: <GPU_NODEPOOL> tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule containers: - name: vllm image: vllm/vllm-openai:v0.21.0 command: - python3 - -m - vllm.entrypoints.openai.api_server args: - --model=s3://<BUCKET_NAME>/<MODEL_PATH> - --load-format=runai_streamer - --model-loader-extra-config={"concurrency":17,"distributed":true} - --tensor-parallel-size=2- --max-model-len=8192 - --host=0.0.0.0 - --port=8000 ports: - containerPort: 8000 name: http env: # Run:ai streamer tuning - name: RUNAI_STREAMER_CHUNK_BYTESIZE value:"4294967296"- name: RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS value:"3000"- name: RUNAI_STREAMER_S3_LOW_SPEED_LIMIT value:"1048576"# torch.compile cache - name: XDG_CACHE_HOME value:"/compile-cache"- name: TORCHINDUCTOR_CACHE_DIR value:"/compile-cache/inductor"- name: TRITON_CACHE_DIR value:"/compile-cache/triton"resources: requests: cpu: "12" memory: 80Gi nvidia.com/gpu: "2" limits: nvidia.com/gpu: "2" volumeMounts: - name: compile-cache mountPath: /compile-cache startupProbe: httpGet: path: /health port: 8000 periodSeconds: 10 failureThreshold: 60 initialDelaySeconds: 30 readinessProbe: httpGet: path: /health port: 8000 periodSeconds: 5 timeoutSeconds: 3 volumes: - name: compile-cache hostPath: path:/mnt/k8s-disks/0/compile-cachetype: DirectoryOrCreate