En resumen: En un rack NVIDIA GB300 NVL72, vLLM con disagregación P/D y Wide-EP sirve DeepSeek-V4.1-Flash en SemiAnalysis AgentX a 954K tokens/s por GPU con un P90 de 196 tokens/s por usuario, 7.9× nuestro resultado en un solo nodo del primer artículo y $38.96 por hora de GPU a los precios fuera de horas pico de DeepSeek. Una ruta de decodificación especializada alcanza 826 tokens/s para un solo usuario. Frente a DeepInfer, el motor propio de DeepSeek, el rendimiento de decodificación de vLLM está dentro del 6% en tamaños de lote de 8–256, y su prefill sigue la curva publicada de DeepInfer.
La eficiencia proviene de una ingeniería deliberada en componentes que vLLM ya tiene, construida sobre los kernels que DeepSeek liberó como código abierto. Este artículo repasa nuestro estudio de rendimiento, desglosa cada optimización y comparte las lecciones que aprendimos:
- Un motor de propósito general puede igualar a uno específico para un modelo
- Entender el modelo antes de optimizar
- Las instancias DEP pequeñas funcionan mejor para el prefill1
- El decode Wide-EP escala bien con EPLB
- La sobrecarga de CPU se vuelve crítica a escala NVL72
- Una ruta de decodificación especializada desbloquea baja latencia
Resumen del rendimiento
Figura 1: SemiAnalysis AgentX en NVIDIA GB300 NVL72: vLLM con disagregación P/D y Wide-EP (dorado) logra un aumento adicional de 7.9× en el rendimiento sobre nuestro resultado agregado en un solo nodo del primer artículo. El rendimiento cuenta los tokens de entrada en caché, de entrada sin caché y de salida (fuente).
En comparación con DeepInfer
DeepInfer es el motor de inferencia propio de DeepSeek para DeepSeek-V4.1-Flash, publicado esta semana como un motor sin conexión con curvas de prefill y decode publicadas. Es la referencia más relevante para este modelo, así que antes de entrar en nuestras optimizaciones mostramos dónde se sitúa vLLM frente a él.
Decode. Bajo la configuración de decode de DeepSeek (contexto de 131K, nextn=3), vLLM en GB200 logra un rendimiento dentro del 6% de los resultados publicados de DeepInfer en tamaños de lote de 8 a 256 (Figura 2).
Figura 2: Rendimiento de salida en decode por GPU frente al tamaño de batch con contexto de 131K, nextn=3. vLLM en GB200 frente a los resultados publicados de DeepInfer, leídos de los gráficos de su README (fuente).
Prefill. La curva de prefill publicada por DeepInfer usa EP4DP4 con 32 expertos redundantes y una replay acotada de 140 tokens, en un hardware que el gráfico no indica, y leemos sus valores del gráfico. Nuestra ejecución usa DEP2 en GB300 sin expertos redundantes, una replay de 128 tokens y nuestros propios prompts. Como las configuraciones difieren, no interpretamos la brecha entre las dos curvas como una aceleración de un motor frente a otro. Lo que muestra la Figura 3 es que el prefill DEP2 de vLLM sigue de cerca al motor propio de DeepSeek en todo el rango que publicaron.
Figura 3: Rendimiento de prefill frente a tokens nuevos. vLLM DEP2 en GB300 frente a la curva publicada de DeepInfer (EP4DP4, 32 expertos redundantes, replay de 140 tokens, hardware no indicado), leída del gráfico en su README (fuente).
Los dos motores arrojan resultados cercanos porque comparten un linaje en ambas direcciones. DeepInfer fue construido originalmente sobre vLLM, y vLLM a su vez ha integrado continuamente kernels de las bibliotecas de DeepSeek, incluidos MegaAttn, MegaMoE y Mega-mHC, y ha optimizado extensamente el resto del stack en torno a ellos. DeepInfer apunta a un solo modelo en modo offline; la misma compilación de vLLM también sirve este modelo online con disagregación P/D y Wide-EP a través de un rack NVL72 completo, que es lo que mide el resto de esta publicación. Las secciones que siguen describen ese trabajo.
Bajo el capó: de dónde viene la velocidad
Primero medir el modelo
Antes de ajustar, comenzamos cuantificando los diseños arquitectónicos eficientes de DeepSeek-V4.1-Flash:
- Huella pequeña de caché KV. Solo 890 bytes por token de caché KV global, gracias a la compartición de KV entre capas y a la compresión FP4 (véase el DeepSeek-V4.1-Flash Technical Report).
- Atención ligera. Las capas 2, 8 y 14 usan compresión 2× para la indexación de contexto completo. Con replay acotada, la capa 20 escribe KV global para todos los tokens pero ejecuta la indexación y la atención solo en los últimos 128; las capas 21–39 procesan solo esos tokens.
- El decode está limitado por el MoE incluso en contexto largo. Véase la Figura 5 más abajo. Con DEP4 decodificando a 128K de contexto y 64 solicitudes por rank, el MoE ocupa el 62% del paso objetivo, mientras que la atención y el indexador juntos ocupan solo el 13%.
La tabla siguiente muestra el tiempo forward del modelo objetivo en DEP32, con 16 solicitudes por rank, 6 tokens por solicitud (un token más cinco draft tokens de DSpark), y todas las solicitudes con la misma longitud de contexto, en GB300. Al pasar de 128K a 855K, un contexto 6.7× mayor solo añade 0.6 ms (+7%) a la latencia de decode del modelo objetivo.
| Contexto por solicitud | 64K | 128K | 256K | 512K | 855K |
|---|---|---|---|---|---|
| Forward del objetivo (ms) | 8.86 | 8.93 | 9.03 | 9.25 | 9.53 |
| vs. 128K | −0.8% | – | +1.1% | +3.6% | +6.7% |
Estas características dan forma al resto de nuestra estrategia de optimización:
- El prefill debe ejecutarse con un tamaño DEP pequeño. Incluso una instancia DEP2 en B300 o GB300 puede albergar tanto los pesos del modelo como la caché KV para muchas sesiones concurrentes. Un world size menor simplifica enormemente el balanceo de carga DP/EP.
- La capacidad KV rara vez limita el tamaño del batch. La caché KV cabe cómodamente en la HBM, lo que nos permite llevar cada operación hacia sus límites de cómputo o ancho de banda de memoria en lugar de sortear la capacidad KV. Esto también mantiene nuestro sweep consistente y con bajo ruido, sin preemption ni presión de la caché KV.
- Wide-EP es eficaz para el decode. El decode de MegaMoE está limitado por las lecturas de pesos, y Wide-EP recorta directamente este coste dominante del MoE.
- El balanceo de carga DP del decode es más simple. El tiempo de paso de decode de este modelo varía poco con la longitud del contexto, por lo que el balanceo de carga DP puede simplificarse a mínima carga por número de solicitudes por rank.
- Las cargas de trabajo sintéticas siguen siendo representativas para el decode. Usar longitudes medias de entrada y salida nos da una buena estimación del rendimiento sin reproducir la traza agéntica completa.
Cómo lo medimos
Medir el rendimiento con precisión sin complicar en exceso la metodología no es trivial. Lo dividimos en dos preguntas: cuánto debería costar un paso de prefill o decode, y cuánta sobrecarga adicional proviene del desequilibrio de DP y EP.
- Tiempo de paso de prefill y decode.
vllm bench forward(#61339) mide el tiempo forward del modelo para un batch exacto de prefill o decode a través de la ruta realexecute_modela través de los ranks de DP y EP. Esto mide solo tiempo de GPU y ofrece una traza limpia. Además, elDecodeBenchConnectormide el rendimiento de decode a nivel de motor de extremo a extremo con una concurrencia dada. La brecha entre los tiempos de paso solo GPU y de serving revela sobrecargas inesperadas. - Desequilibrio DP del prefill: replays espejo vs. con retraso. Las trazas agénticas varían de una ejecución a otra, por lo que el desequilibrio DP es difícil de aislar. Por ello reproducimos solicitudes idénticas en cada rank de DP. En una ejecución espejo , todos los ranks reciben sus solicitudes al mismo tiempo. En una ejecución con retraso , el rank r recibe las mismas solicitudes r × 5 s más tarde, por lo que los rangos están desfasados pero realizan el mismo trabajo total. La diferencia entre las dos ejecuciones es el costo del desbalance de DP.
- Desbalance de EP en decodificación: enrutamiento real vs. uniforme. Para cada capa MoE, definimos el balanceo como la carga media por rango dividida por la carga máxima por rango, por paso y a lo largo de la ventana de EPLB. Como referencia balanceada, ejecutamos el mismo lote de decodificación con tokens enrutados uniformemente al azar. Esta referencia todavía tiene ruido de muestreo: en DEP32 con 72 solicitudes por GPU, el enrutamiento uniforme alcanza un balanceo de aproximadamente 0.96 para el modelo objetivo (enrutamiento top-6) y 0.94 para el borrador (top-3). La brecha entre el enrutamiento real y el uniforme es lo máximo que puede recuperar el balanceo de expertos, y la ejecución de EPLB muestra cuánto de esa brecha recupera realmente.
Prefill: DEP2 funciona mejor
Como se comentó anteriormente, un tamaño DEP pequeño es atractivo para el prefill. En una ejecución de solo prefill de AgentX a una concurrencia de 256, DEP4 dio solo el 83% del rendimiento por GPU de DEP2 (53.3K frente a 64.4K tokens/s). Las repeticiones mirror y lag con el almacenamiento en caché de prefijos desactivado apuntan al desbalance de DP: cuando los rangos se desfasan, DEP4 pierde el 13.6% de su rendimiento.2 Esto hace que DEP2 sea un valor predeterminado simple para nosotros.
| Rendimiento de prefill por instancia (tokens/s) | mirror (balanceado) | lag (desequilibrado) | pérdida |
|---|---|---|---|
| DEP2 | 138.7K | 130.5K | 5.9% |
| DEP4 | 267.6K | 231.1K | 13.6% |
Optimizaciones ocultas tras la traza
Nuestra primera publicación cubrió un amplio conjunto de optimizaciones de prefill. Trazas más limpias nos ayudaron a identificar los cuellos de botella restantes, en particular los que crecen con la longitud del contexto. Por ejemplo, en dos GPU GB300, un prefill de 8K tokens toma 87.9 ms sin contexto previo, pero 189.4 ms con 512K de contexto en caché. Abordamos tres cuellos de botella adicionales:
Extender el replay acotado de SWA a la capa 20 (#59894): +19.9% a 32K tokens nuevosEl replay acotado de SWA ya limita las capas 21–39 a los últimos 128 tokens. Anteriormente, la capa 20 todavía realizaba el cálculo del indexador sobre todos los tokens nuevos. Ahora escribe KV para todos los tokens nuevos, pero ejecuta el resto de su cálculo solo sobre el lote de replay de 128 tokens. Esto mejora el rendimiento de prefill en 19.9% a 32K tokens nuevos, con ganancias que aumentan desde 10.1% a 4K hasta 23.1% a 64K.
Usar páginas de 2 MB para tablas host compartidas de Engram (#62359): otros +2.8% a 32KEngram realiza lecturas aleatorias de tablas host compartidas entre rangos DP. El mapeo anterior /dev/shm usaba páginas GPU de 64 KB, lo que causaba frecuentes fallos de TLB en la GPU. Mover las tablas a asignaciones cuMem en host-NUMA permite páginas GPU de 2 MB, mejorando el rendimiento de prefill en otros 2.8% a 32K tokens nuevos.
El indexador de prefill ahora usa DeepSelect para clasificar los bloques candidatos, coincidiendo con la ruta de decode. Su beneficio crece con el número de claves clasificadas, mejorando el rendimiento de prefill en otros 15.6% a 32K tokens nuevos y 15.2% a 64K.
En conjunto, estos cambios mejoran el rendimiento de prefill de tokens nuevos en 35.6–43.9% en el rango de 4K–64K tokens nuevos sobre un prefijo en caché de 128K. A 32K, la ganancia combinada es 42.5%; a 64K, el rendimiento aumenta de 71.2K a 102.5K tokens/s/GPU (+43.9%).
Figura 4: Rendimiento de prefill de tokens nuevos por GPU en DEP2 tras un acierto de caché de 128K, añadiendo el replay de la capa 20, páginas de 2 MB de Engram y DeepSelect top-k uno a la vez.
Decode: escalando Wide-EP y equilibrando expertos con EPLB
En el lado del decode, Wide-EP con EPLB es una práctica consolidada, descrita en la visión general del sistema de inferencia de DeepSeek. vLLM ha admitido ambos desde hace tiempo (véase nuestra entrada sobre servicio a gran escala), y descubrimos que siguen funcionando bien. Esta sección se centra en nuestros nuevos hallazgos y lecciones empíricas.
El escalado de Wide-EP
Medimos el escalado de decodificación de DEP4 a DEP32 con 64 solicitudes por GPU a 128K de contexto (con EPLB activado; véase la siguiente sección). El tiempo objetivo de MegaMoE baja de 13.4 a 6.0 ms a medida que cada rango contiene menos expertos, mientras que el trabajo restante se mantiene en torno a 10 ms. Como resultado, las ganancias de rendimiento por GPU disminuyen gradualmente. En DEP32, la sobrecarga de comunicación del draft también empieza a compensar los beneficios de un paralelismo más amplio.
Figura 5: Tiempo de paso de decodificación por rango de DEP4 a DEP32 en NVIDIA GB200 con 64 solicitudes por GPU a 128K de contexto, y rendimiento de salida por GPU.
EPLB
Permitimos que EPLB reorganice los expertos cada 3,000 pasos, basándose en la carga de los 1,000 pasos anteriores. EPLB eleva el equilibrio de 0.35 a 0.94 para el target y de 0.21 a 0.89 para el draft, recuperando alrededor del 97% y del 93% de la brecha respecto al enrutamiento uniforme.
Figura 6: Decodificación en GB300 NVL72 con EPLB desactivado, EPLB solo para el modelo target y EPLB para el target y el draft DSpark. Las pestañas muestran el equilibrio por paso, el desglose de tiempos por paso en DEP32 y el rendimiento (media de tres ejecuciones; las barras muestran el rango).
Dos cambios hacen que esto funcione. #62009 introduce un backend basado en NVLink symm_mem que reequilibra todo DEP32 en menos de un segundo sin detener la decodificación, fusiona el mapeo de réplicas y el registro de carga de EPLB en los kernels de MegaMoE (ahorrando unos 0.2 ms por paso) y elimina la sincronización del host del bucle de decodificación. #61395 admite EPLB con modelos target y draft heterogéneos. El draft DSpark tiene 128 expertos enrutados con enrutamiento top-3, frente a los 384 del target con top-6, por lo que EPLB realiza un seguimiento por separado de la ubicación y la carga de los expertos para cada modelo y reequilibra ambos.
Superar la sobrecarga de CPU en la decodificación rápida a gran escala
La decodificación Wide-EP sincroniza todos los rangos en cada capa, por lo que un rango lento bloquea la instancia. La programación asíncrona oculta bien la sobrecarga de CPU a pequeña escala, pero más rangos lo hacen más difícil de dos maneras: los pasos de GPU se acortan, lo que deja menos margen para el trabajo de CPU, y es más probable que un rango se quede atrás.
En la práctica, observamos que un DEP32 sin optimizar corría más lento que DEP16 (17.57 frente a 13.84 ms por paso), aunque su ejecución en GPU era 1.1 ms más rápida. La siguiente tabla enumera todas las causas que encontramos, ordenadas por efecto. Los efectos provienen de configuraciones diferentes, por lo que cada fila indica la suya.
Mostrar las causas, las correcciones y sus efectos| Causa | Corrección | PRs | Efecto | Configuración |
|---|---|---|---|---|
| Lectura de KV publicada de forma síncrona en el hilo del worker de decodificación | NIXL push con UCX scatter-gather; filas de KV empaquetadas enviadas como una única región NIXL | Conector push de NIXL, #61121 | 141K → 440K tokens/s por GPU; P90 ~40 → 159 tokens/s por usuario | GB300, 8P1D, concurrencia 3,072 |
| La latencia de all-reduce de Gloo crece linealmente con el número de ranks | All-reduce de CPU por duplicación recursiva | #61767, basándose en #60267 | Sincronización de DP 10.8 → 1.5 ms por paso; paso 16.10 → 13.15 ms | Decodificación DEP32 |
| Ráfagas de admisión: hasta 140 solicitudes admitidas en un paso | Límite opcional de 4 nuevas cargas de KV por paso | #61725 | 460K → 516K tokens/s por GPU; P90 109 → 153 tokens/s por usuario | GB300, 7P1D, concurrencia 4,800, con otras dos correcciones de CPU |
| Trabajo de admisión que crece con la longitud del prompt | Prompt como un único buffer int32; omitir los bloques nulos de las tablas de bloques de ventana deslizante; kernel de copia de prompt por fragmentos | #59908, #62702, #61724, #62698 | Prompt de 855K: 38.8 → 6.0 ms de CPU por admisión | Microbenchmark del motor |
Baja latencia: una ruta de decodificación especializada para lotes pequeños
También ampliamos el soporte de kernels de baja latencia para DeepSeek-V4.1-Flash (#61868 y #61869), continuando los esfuerzos de nuestra publicación anterior. En sesiones reales de AgentX, la ruta de decodificación especializada alcanza 826 P90 tokens/s para un solo usuario en cuatro GPU B300, un 44% más que la ruta de decodificación estándar (575).
Figura 7: SemiAnalysis AgentX con TP4 en cuatro GPU B300 a concurrencia 1, 8 y 16, antes y después de que InferenceX #4332 cambiara a la ruta de decodificación especializada.
Agradecimientos
Nos gustaría agradecer a DeepSeek por publicar como código abierto y compartir DeepSeek-V4.1-Flash y sus kernels optimizados, que proporcionaron la base para este trabajo, y por publicar DeepInfer y sus resultados de rendimiento, que nos dieron una sólida referencia de la que aprender y con la que comparar. También agradecemos a SemiAnalysis por la plataforma de benchmark AgentX y toda la ayuda.
Estamos especialmente agradecidos con Pei Li, Hanjie Qiu, Lisa Wang, Yaoyao Ding, Rohit Nagraj y Xin Li de NVIDIA por su estrecha colaboración en la optimización del rendimiento, el apoyo continuo en vLLM y Dynamo, y la asistencia con las entregas de AgentX. También agradecemos a Canlin Guo de la comunidad de vLLM por su valiosa optimización del prefill.
Este trabajo fue liderado por Inferact.
Notas al pie
-
DEPn es atención en paralelo de datos más paralelismo de expertos sobre n GPU. El forward objetivo es el pase forward completo del modelo principal en cada paso de decodificación, que verifica los tokens borrador y produce la salida; el forward del draft es el pase forward completo del modelo borrador DSpark, que propone los tokens para el siguiente paso. ↩
-
Medido en una versión anterior; el costo del desbalance es una propiedad del tamaño de DEP, no de los kernels. ↩