Menor latencia y mayor rendimiento con la implementación multinodo de DeepSeek
La implementación multi-GPU impulsa el rendimiento de MoE en velocidad y escala
En la mayoría de los sistemas, la latencia y el rendimiento suelen ser objetivos contradictorios que requieren concesiones durante el diseño y la implementación. Por ejemplo, en los modelos densos de lenguaje grande, aumentar el tamaño del lote puede mejorar el rendimiento, pero también incrementa la latencia; aumentar el paralelismo de tensores dentro de una sola máquina puede reducir la latencia, pero disminuye el número de réplicas, lo que genera un menor rendimiento.
Los modelos de mezcla de expertos (MoE), como DeepSeek-V3/R1, han demostrado recientemente excelentes capacidades y eficiencia operativa. Por ejemplo, el modelo DeepSeek-V3/R1 tiene un total de 671 mil millones de parámetros, pero cada token utiliza solo 37 mil millones de parámetros durante la inferencia. Esta arquitectura de modelo presenta tanto desafíos como oportunidades para los sistemas de inferencia.
Este artículo demuestra que, al contrario de los sistemas convencionales, los modelos MoE como DeepSeek-V3/R1 pueden alcanzar simultáneamente un mayor rendimiento y una menor latencia al utilizar más GPUs en implementaciones multinodo en la mayoría de los escenarios.
Arquitecturas de implementación
Debido a la gran cantidad de pequeños expertos que posee el modelo, las implementaciones deben distribuirse en múltiples dispositivos. Consideramos tanto implementaciones en un solo nodo con 8 GPUs H200 como implementaciones multinodo en 8 GPUs H100.
Ambas arquitecturas de implementación aprovechan el paralelismo de datos, orquestado a través de nuestro programador de solicitudes interno. La implementación del paralelismo de datos implica el lanzamiento de múltiples instancias del motor de inferencia, cada una operando de manera independiente para atender y mantener las solicitudes. El programador de solicitudes, que interactúa con el motor a través de gRPC, se encarga de distribuir las solicitudes de la manera más uniforme posible, a la vez que facilita la reutilización de la caché KV, enviando solicitudes con un prefijo parcialmente coincidente a los servidores que contienen la caché. Las instancias del motor no abarcan múltiples nodos. Opcionalmente, pueden utilizar el paralelismo de tensores para fragmentar la atención en múltiples dispositivos. Las instancias están interconectadas mediante NVLink en el caso de un solo nodo o InfiniBand para el caso multinodo, despachando y recopilando expertos.
La configuración de implementación en un solo nodo ofrece una latencia superior con tamaños de lote pequeños; sin embargo, el rendimiento se degrada rápidamente bajo condiciones de carga elevada.
Para implementar el motor de servicio, lanzamos un pod por nodo que aloja múltiples instancias del motor. PyTorch se encarga de configurar la comunicación distribuida y negociar la inicialización de NVSHMEM. Para la comunicación, nos basamos en los kernels de CUDA personalizados descritos en una entrada de blog anterior. La implementación de las dos implementaciones es prácticamente idéntica, y el modelo selecciona los kernels correctos que debe usar según la infraestructura que implementa el paralelismo de expertos.
Técnicas de paralelización
Antes de sumergirnos en nuestras comparaciones de rendimiento, es fundamental comprender las estrategias clave de paralelización que hacen posible implementar modelos MoE masivos como DeepSeek-V3/R1.
Paralelismo de tensores
En la inferencia de LLM, el paralelismo de tensores (TP) se utiliza típicamente para reducir el uso de memoria y el cómputo por GPU, reduciendo así la latencia. Por lo general, podemos fragmentar las proyecciones Linear en las capas de atención y MLP a lo largo de las dimensiones de filas o columnas, y fragmentar las operaciones de atención a lo largo de la dimensión de la cabeza de atención.
Con el TP, la arquitectura Llama-3 no tiene cómputo duplicado para la proyección Linear y las operaciones de atención en las GPUs, lo cual es un método de fragmentación ideal. Sin embargo, en los modelos DeepSeek-V3/R1, el TP no puede lograr esto.
Los modelos DeepSeek-V3/R1 utilizan la atención multilatente (MLA). Una capa MLA primero utiliza una proyección Linear kv_a_proj para calcular el vector latente, y luego utiliza otra proyección Linear kv_b_proj para transformarlo en el espacio de cada cabeza de atención. Dado que todas las cabezas de atención comparten el mismo vector latente, el TP no puede fragmentar el vector latente, por lo que todos los rangos de TP necesitan replicar los parámetros y el cómputo de kv_a_proj y kv_b_proj. De manera similar, dado que MLA almacena el vector latente en la caché KV, cada rango de TP almacena una copia idéntica de la caché KV.
A pesar de cierta duplicación en MLA, el paralelismo de tensores aún proporciona una reducción parcial en las demandas de cómputo, lo que lo hace valioso para escenarios que requieren altas velocidades de salida.
Paralelismo de expertos
Los modelos DeepSeek-V3/R1 reemplazan las capas MLP con capas MoE. Una capa MoE tiene 256 expertos enrutados y un experto compartido. Cada token se despacha a 8 expertos enrutados diferentes para su cómputo, y los resultados se suman de manera ponderada. Cada token también se calcula en el experto compartido, y el resultado se suma al resultado de los expertos enrutados.
El paralelismo de expertos (EP) sirve como el enfoque de fragmentación típico para las capas MoE, donde cada GPU administra 256 / EP expertos enrutados mientras mantiene una copia del experto compartido. En comparación con el TP, la ventaja del EP es que puede distribuir el cómputo en más GPUs, lo que reduce el cómputo y el uso de memoria por GPU.
Antes de realizar el cómputo de expertos, todas las GPUs deben realizar una comunicación AllToAll para despachar los tokens a las GPUs donde se encuentran los expertos correspondientes; después del cómputo de expertos, se necesita otra comunicación AllToAll para recopilar los resultados de cómputo de varias GPUs y realizar la suma ponderada. Implementamos una versión optimizada de estos dos kernels de comunicación AllToAll, Dispatch y Combine, utilizando NVSHMEM. En nuestra entrada de blog anterior, detallamos la implementación, y nuestros kernels han sido publicados en código abierto en GitHub.
Paralelismo de datos
Con el EP, podemos distribuir el cómputo de MoE en 128 o incluso más GPUs. Sin embargo, el cómputo de MLA no se puede particionar con el EP. En este punto, podemos introducir el paralelismo de datos (DP). Cada grupo de DP tiene una copia completa de la capa MLA. Cada grupo de DP acepta diferentes entradas y realiza el cómputo de la capa MLA de forma independiente.
El DP y el TP de la capa MLA se pueden combinar, dividiendo un grupo de DP en múltiples rangos de TP. El EP de la capa MoE se puede combinar con el DP/TP de la capa MLA. EP = DP * TP. Por ejemplo, en 16 máquinas, EP128 DP32 TP4 significa distribuir los expertos enrutados en 128 GPUs, con cada 4 GPUs formando un grupo de DP, para un total de 32 grupos de DP independientes.
Un solo nodo frente a multinodo
Los 671 mil millones de parámetros de DeepSeek superan la capacidad de memoria de una sola máquina H100 de 8 GPUs (80 GB * 8), pero una sola máquina H200 de 8 GPUs puede acomodar por completo todo el modelo (141 GB * 8). Utilizando la configuración EP8 DP8 TP1, el modelo utiliza unos 100 GB de memoria por GPU, dejando aproximadamente 40 GB para la caché KV y otros resultados intermedios. Un token ocupa 70 272 bytes de caché KV. Suponiendo que cada solicitud tiene 5000 tokens, cada GPU puede acomodar aproximadamente 100 solicitudes.
Queríamos comprender las diferencias de rendimiento entre las implementaciones en un solo nodo y las multinodo bajo diferentes configuraciones. Utilizamos una máquina H200 para la implementación en un solo nodo y hasta 16 máquinas H100 para las implementaciones multinodo. Para cada entorno de implementación, utilizamos combinaciones de TP 1, 2, 4, 8 y tamaños de lote por GPU de 1, 2, 4, 8, 16, 32, 64 y 128. Asumimos que cada solicitud tenía una longitud de caché KV de 5000 tokens. También asumimos que la predicción multitoken (MTP) predice 1 token adicional (es decir, la longitud de la consulta de cada solicitud es 2), y asumimos conservadoramente una tasa de aceptación del 60%. La siguiente figura muestra el rendimiento y la velocidad de salida para diferentes configuraciones.
El eje horizontal representa la velocidad de salida por solicitud en tokens/s. El eje vertical utiliza una escala logarítmica para mostrar el rendimiento por máquina en tokens/s. Marcamos la frontera de Pareto para cada configuración de EP con líneas de diferentes colores.
En escenarios con requisitos de velocidad de salida extremadamente altos, el uso de EP8 DP1 TP8 en un solo nodo con un tamaño de lote de 1 puede lograr una velocidad de salida que supera los 100 tokens/s, pero el rendimiento es extremadamente bajo, equivalente a la velocidad de salida. En este escenario, todo el lote tiene solo 2 tokens, los cuales se pueden despachar a como máximo 2*8=16 expertos, activando un total de como máximo 57 mil millones de parámetros.
En el rango de velocidad de salida de 80-40 tokens/s, a medida que aumenta el rendimiento, la velocidad de salida disminuye significativamente. En contraste, EP128 tiene un rendimiento aproximadamente 5 veces mayor que la implementación en un solo nodo a la misma velocidad de salida.
Este fenómeno se puede explicar examinando cómo se comportan las implementaciones en un solo nodo: aumentar el tamaño del lote se correlaciona directamente con un aumento en los expertos activados. Cuando el tamaño del lote es 1, el número promedio de expertos activados por GPU es 2 * 8 / 8 = 2. Cuando el lote es lo suficientemente grande, todos los expertos se activan, lo que significa que cada GPU activa 256 / 8 = 32 expertos. Activar más expertos significa que la GPU necesita leer más parámetros de la memoria, lo que aumenta significativamente la presión sobre el ancho de banda de la memoria. Dado que la fase de decodificación de los modelos de lenguaje grande ya está limitada por el ancho de banda de la memoria en lugar del rendimiento de cómputo, aumentar el tamaño del lote en la implementación en un solo nodo reduce significativamente la velocidad de salida.
La comparación de las cuatro configuraciones de implementación multinodo (EP16, EP32, EP64 y EP128) revela que los valores de EP más altos desplazan la frontera de Pareto hacia mejoras simultáneas en el rendimiento y la velocidad de salida.
Utilizar un número de EP más alto significa que a cada GPU se le asignan menos expertos. Por ejemplo, EP128 significa que cada GPU es responsable de 256 / 128 = 2 expertos, por lo que la presión sobre el ancho de banda de la memoria se reduce significativamente. En otras palabras, al utilizar un número de EP mayor, obtenemos de manera efectiva más ancho de banda de memoria. Cuando el tamaño del lote por GPU es inferior a 64, aumentar el tamaño del lote no afecta significativamente la velocidad de cómputo de los expertos, ya que aumentar el número de entradas no incrementa de forma notable la presión sobre el ancho de banda de la memoria. Por lo tanto, observamos que al utilizar EP128, aumentar el tamaño del lote no afecta la velocidad de salida de forma tan significativa.
Curiosamente, con tamaños de lote más grandes (64 solicitudes por GPU), observamos un nuevo fenómeno: el rendimiento de la implementación en un solo nodo es ligeramente superior al de la implementación multinodo. Parte de la razón es que NVLink intranodo tiene un mayor ancho de banda que InfiniBand internodo. Otra parte se debe a limitaciones en nuestra implementación. Analizaremos este fenómeno con más detalle más adelante.
Debido a limitaciones en la capacidad de memoria, la configuración EP8 DP8 TP1 no puede alcanzar un tamaño de lote de 128 por GPU, por lo que la implementación multinodo sigue siendo una mejor opción en escenarios que buscan un mayor rendimiento.
Solapamiento de cómputo y comunicación
Como se introdujo brevemente arriba con respecto al paralelismo de expertos, las GPUs están inactivas durante la comunicación de la capa MoE. Para reducir el desperdicio y disminuir la latencia, necesitamos encontrar tareas de cómputo independientes de los datos para llenar este tiempo inactivo.
La parte superior de la figura anterior muestra el flujo de cómputo de una capa. El cómputo de MoE depende de Dispatch, y el cómputo de la siguiente capa depende del resultado de Combine.
Colocamos el experto compartido en cada GPU. De esta manera, el cómputo del experto compartido no requiere comunicación AllToAll. Por lo tanto, podemos realizar el cómputo del experto compartido inmediatamente después de Dispatch Send, y luego esperar a que se complete Dispatch Recv. Llamamos a este esquema de solapamiento "Dispatch Overlap".
Dispatch Overlap ofrece una implementación sencilla y una amplia aplicabilidad. Esta técnica oculta el tiempo de cómputo del experto compartido en todos los tamaños de EP y tamaños de lote.
Para aumentar aún más el solapamiento de cómputo y comunicación, utilizamos microlotes mencionados en el informe técnico de DeepSeek para romper la dependencia de los datos. Como se muestra en la parte inferior de la figura, dividimos el cómputo de una capa Transformer en 5 etapas:
Etapa 1: InputNorm, QKVProj, AppendKV, BMM
Etapa 2: BMM, Attn, OProj, PostNorm, Gate
Etapa 3: Dispatch Send, experto compartido
Etapa 4: Dispatch Recv, MoE, Combine Send
Etapa 5: Combine Recv
En las primeras 3 capas Transformer densas, utilizamos el lote completo. En las siguientes 58 capas Transformer MoE, dividimos uniformemente el lote en dos microlotes. Los dos microlotes se ejecutan de forma alterna, con un desfase de 3 etapas. Dado que no existe dependencia de datos entre estos dos microlotes, podemos cambiar al cómputo de otro microlote después de Dispatch Send y después de Combine Send.
Desglose de latencia
A continuación, comparamos los efectos del solapamiento mediante un experimento, así como las diferencias de rendimiento entre la implementación en un solo nodo EP8 y la implementación multinodo EP128. Para facilitar la comparación, utilizamos GPUs H100 para el siguiente experimento. Utilizamos TP1, un tamaño de lote de 128 por GPU, una longitud de consulta de 2 por solicitud y una longitud de caché KV de 5000.
La figura anterior muestra el tiempo total empleado en una capa Transformer MoE y la proporción de latencia de diferentes tipos de kernels. A excepción de Dispatch, Combine y GroupGEMM, el tiempo de ejecución de los otros kernels debe ser el mismo en las series EP8, EP128 NoOverlap y EP128 DispatchOverlap porque el tamaño del lote es el mismo.
Solapamiento
Comparecemos primero los efectos de los tres métodos de solapamiento. NoOverlap tomó 2667 µs en total, DispatchOverlap tomó 2651 µs, lo que ahorra 16 µs o solo un 0.6 %. MicroBatch mostró una mejora muy significativa, tomando 1896 µs, una aceleración del 29 %. Tanto el tiempo de Dispatch como el de Combine se redujeron significativamente. Dispatch disminuyó de 593 µs a 367 µs, y Combine de 1012 µs a 237 µs.
Tenga en cuenta que para los kernels de cómputo, dividir un lote de tamaño 128 en dos lotes de tamaño 64 aumenta el tiempo total de ejecución. Por lo tanto, aunque el tiempo dedicado a la comunicación se redujo en 1001 µs, el tiempo total solo se redujo en 771 µs. Explicaremos la razón utilizando el modelo Roofline en la siguiente sección.
Por esta razón, el uso de microlotes no siempre mejora el rendimiento.
La figura anterior muestra la mejora de rendimiento de Microbatch en comparación con DispatchOverlap para tamaños de lote de 4 a 128. Cuando el tamaño del lote es inferior a 32, Microbatch disminuye el rendimiento entre un 5 % y un 40 %. Cuando el tamaño del lote es mayor o igual a 32, Microbatch puede mejorar el rendimiento entre un 10 % y un 35 %.
EP8 frente a EP128
Volvamos a la figura anterior y comparemos EP8 y EP128 Microbatch. EP8 tomó 1802 µs en total, ligeramente menos que los 1896 µs de EP128. A aparte del mayor tiempo de ejecución del kernel aportado por Microbatch mencionado anteriormente, las principales diferencias se encuentran en GroupGEMM utilizado para el cómputo de MoE y en los dos kernels de comunicación, Dispatch y Combine.
GroupGEMM de EP8 tomó 555 µs, mientras que GroupGEMM de EP128 tomó 270 µs, reduciéndose a la mitad. Esta es la ventaja principal de la implementación multinodo.
Desafortunadamente, el tiempo dedicado a la comunicación aumentó en 213 µs, lo que compensó en gran medida la ventaja de GroupGEMM. En pruebas de rendimiento independientes de nuestros kernels de comunicación, descubrimos que solo pueden alcanzar la mitad del ancho de banda de InfiniBand. Continuaremos optimizando nuestros kernels de comunicación.
Otro kernel que se queda significativamente atrás es GEMM. Microbatch aumentó GEMM en 95 µs. Analizaremos GEMM con más profundidad en la sección Roofline a continuación. Creemos que la implementación actual de GEMM aún no ha alcanzado un rendimiento óptimo.
Roofline
El modelo Roofline es una buena herramienta para analizar el rendimiento del kernel. Su eje horizontal es la intensidad aritmética, la relación entre FLOP y los bytes de E/S de memoria. El valor del eje horizontal se puede calcular directamente a partir de la semántica del kernel. El eje vertical representa el rendimiento alcanzado, calculado dividiendo FLOP por la latencia de referencia.
El límite superior teórico del rendimiento del kernel está determinado directamente por las especificaciones de la GPU. El rendimiento máximo en FP8 de la H100 es de 1979 TFLOP/s, representado como una línea horizontal en el modelo Roofline. El ancho de banda de memoria de la H100 es de 3.35 TB/s, representado como la pendiente de una línea que pasa por el origen. Las dos líneas proporcionan los límites de rendimiento para los kernels limitados por cómputo y limitados por memoria, respectivamente.
A continuación, analizamos el rendimiento de los kernels GroupGEMM y GEMM.
GroupGEMM
El kernel GroupGEMM en MoE realiza el siguiente cómputo: Hay un total de g grupos, el i-ésimo grupo tiene m_i tokens, realizando una multiplicación de matrices de [m_i, k] x [k, n] -> [m_i, n]. En las pruebas de rendimiento, asumimos que el número de tokens en cada grupo es el mismo, denotado como m_i = m. Entonces, el recuento de FLOP para GroupGEMM es 2 * g * m * k * n, y los bytes de E/S de memoria son g * (m * k + n * k + m * n).
En el modelo DeepSeek-V3/R1, hay 256 expertos, y cada token se despacha a 8 expertos para su cómputo. Suponiendo un tamaño de lote de 128, una longitud de consulta de 2, utilizando la configuración EP128 DP128, el número promedio de tokens recibidos por cada experto (es decir, m) es 128 * 2 * 8 * 128 / 256 = 1024. De manera similar, podemos calcular m para otras configuraciones y tamaños de lote.
Utilizamos la implementación GroupGEMM de DeepGEMM para las pruebas de rendimiento. Los puntos de prueba cubrieron combinaciones de configuraciones EP8, EP16, EP32, EP64, EP128 con TP1 y tamaños de lote de 1 a 128.
La figura anterior muestra el modelo Roofline para GroupGEMM bajo diferentes configuraciones de EP. Diferentes EP corresponden a diferentes números de grupos. La figura ilustra líneas de rendimiento casi superpuestas, lo que indica que el rendimiento de GroupGEMM está determinado predominantemente por el recuento total de tokens (representado como g * m).
Las estrellas marcan los puntos de datos correspondientes a un tamaño de lote de 128 por GPU para cada configuración de EP. Al comparar estos puntos de datos con estrellas, podemos ver que a medida que aumenta el EP (y el DP aumenta sincrónicamente), el número de tokens por experto m también aumenta. En EP8, m=128, mientras que en EP128, m=2048.
A medida que m aumenta, la intensidad aritmética también aumenta. En la mayoría de las configuraciones, GroupGEMM está limitado por el ancho de banda de la memoria, por lo que aumentar m mejora el rendimiento.
GEMM
El kernel GEMM corresponde a las proyecciones Linear en el modelo, como la proyección Q/K/V/O. Para una multiplicación de matrices de [m, k] x [k, n] -> [m, n], el recuento de FLOP es 2 * m * k * n, y los bytes de E/S de memoria son m * k + n * k + m * n. También podemos probar la latencia para tamaños de lote de 1 a 128.
La figura anterior muestra el modelo Roofline para GEMM bajo diferentes configuraciones de EP. Podemos ver que el rendimiento de GEMM está limitado por el ancho de banda de la memoria. A medida que aumenta el tamaño del lote, la intensidad aritmética también aumenta, mejorando así el rendimiento.
Microbatch
Al utilizar microlotes, dividimos el lote uniformemente en dos partes. A partir de las dos figuras anteriores, podemos ver que cuando m se convierte en m/2, la eficiencia de la multiplicación de matrices disminuye. Por lo tanto, ejecutar dos multiplicaciones de matrices de tamaño m/2 toma más tiempo que ejecutar una multiplicación de matrices de tamaño m.
Predicción multitoken
A lo largo de este artículo, hemos asumido el uso de la predicción multitoken (MTP) para la decodificación especulativa. MTP cambia la longitud de la consulta por solicitud de 1 a 2. Para la multiplicación de matrices, esto equivale a cambiar m a m * 2, aumentando así la eficiencia de la multiplicación de matrices. Por otro lado, si dibujamos el modelo Roofline para MLA, descubriríamos que aumentar la longitud de la consulta incrementa significativamente la eficiencia del kernel MLA.
Por lo tanto, el uso de MTP juega un papel importante en la eficiencia del modelo.
Implementación y optimizaciones
En esta sección, presentaremos algunos detalles de implementación y optimización para nuestro modelo DeepSeek-V3/R1.
Cuantización
DeepSeek-V3/R1 se entrenó de forma nativa en FP8 utilizando un esquema de cuantización por bloque, con los pesos cuantizados estáticamente y las activaciones cuantizadas sobre la marcha. En lugar de calcular un factor de escala por canal o por matriz estáticamente, los factores de escala se calculan a través de vectores de 128 elementos para las activaciones y mosaicos de 128x128 elementos para las matrices, lo que limita la degradación de la precisión debido a la cuantización.
En Perplexity, nos basamos en una combinación de kernels de CUDA y Triton para respaldar la inferencia, utilizando CUDA para los kernels más sensibles al rendimiento y modificados con poca frecuencia (como la atención y GEMM), mientras que Triton implementa una amplia gama de kernels de activación, normalización y utilidad. Triton nos permitió adaptar rápidamente los kernels al esquema de cuantización por bloque.
Para las capas lineales y de MoE, mezclamos los kernels de Deep GEMM con nuestros propios kernels GEMM de Triton, ya que hemos notado que para ciertas dimensiones de matriz y tamaños de lote bajos, Split-K ofrece una menor latencia. Si la capa no cuantizada realiza una multiplicación (M, K) x (K, N), necesita un factor de escala (M x ceil_div(K, 128)) x (ceil_div(K, 128), ceil_div(N, 128)) para la cuantización por bloque. Para la cuantización por bloque, los factores de escala para las activaciones se calculan sobre la marcha, en lugar de precalibrarse. Dado que los factores de escala de activación se agregan solo a lo largo de la dimensión K y no a lo largo de la dimensión M, los kernels requieren solo ligeras alteraciones para admitir el esquema.
La función de activación SiLU utilizada por DeepSeek-V3/R1 requirió cambios sustanciales para admitir gráficos de CUDA, cuantización por bloque y recuentos de tokens enrutados dinámicamente. La cuantización por bloque puede ser problemática, ya que introduce reducciones horizontales; sin embargo, el kernel ya dividía las activaciones a lo largo de su dimensión oculta en bloques de 1024 elementos. Dentro de un bloque, el tensor que se va a cuantizar se dividía aún más en bloques de 128 para calcular el valor absoluto más grande, con Triton generando reducciones máximas cross-warp eficientes, agregando una sobrecarga mínima.
Para admitir el enrutamiento de MoE bajo gráficos de CUDA, los kernels deben conocer la información de enrutamiento que indica el número de tokens por experto, en lugar de programar el trabajo en función del tamaño de los búferes que se asignaron para contener el límite superior de los recuentos de tokens. No podemos dividir el problema en función de las dimensiones del tensor de entrada, por lo que lanzamos un número fijo de kernels persistentes que leen la información de enrutamiento para determinar cuántos tokens están poblados y dividen el trabajo de procesamiento de las activaciones entre ellos de forma dinámica.
Ya hemos integrado algunos de nuestros kernels en el proyecto FlashInfer y, en el futuro, liberaremos más de nuestro código como código abierto.
Capa MLA
Utilizamos FlashInfer para el cómputo de MLA. FlashInfer admite configuraciones flexibles de tablas de páginas y un rendimiento extremadamente alto.
Fusionamos q_a_proj y kv_a_proj en un solo qkv_a_proj. La latencia disminuyó de 15.4 µs + 14.8 µs = 30.2 µs a 16.7 µs.
Descompusimos kv_b_proj en dos matrices, k_b_proj y v_b_proj. Escribimos un kernel BMM cuantizado por bloque en FP8 para los cálculos relacionados con estas dos matrices.
Gráfico de CUDA
El gráfico de CUDA puede reducir significativamente la sobrecarga de lanzamiento de kernels, lo cual es crucial para el rendimiento. Creamos un gráfico de CUDA para cada tamaño de lote.
Antes de desarrollar nuestro kernel AllToAll, utilizamos torch.all_to_all_single() para la comunicación AllToAll. Esta operación requiere que todas las GPUs utilicen el mismo tamaño de lote. Sin embargo, diferentes grupos de DP pueden ejecutar diferentes tamaños de lote.
Para garantizar que all_to_all_single() sea compatible con diferentes grupos de DP que utilizan diferentes tamaños de lote, primero utilizamos una operación allreduce() antes de cada ejecución del modelo para obtener el tamaño de lote máximo entre todos los grupos de DP. Luego, hicimos que todos los grupos de DP utilizaran este tamaño de lote para ejecutarse.
Aunque este enfoque garantiza que podamos utilizar el gráfico de CUDA, tiene tres desventajas. Primero, requiere una operación allreduce() adicional. Segundo, los grupos de DP con tamaños de lote más pequeños se ven obligados a rellenarse (padding). Tercero, hace que nuestro código de implementación sea complejo.
Después de implementar nuestro propio kernel AllToAll, ya no requerimos que todas las GPUs utilicen el mismo tamaño de lote. Por lo tanto, ya no necesitamos realizar operaciones allreduce() adicionales ni rellenar los tamaños de lote.
Enrutador MoE
El enrutador MoE está implementado en Triton y se basa en una ordenación modificada derivada de la biblioteca estándar que también realiza un seguimiento de los índices de los elementos ordenados. La implementación se comparte en todos los modelos MoE, ya que el enrutamiento de Mixtral es un caso especial de las rutas de DeepSeek donde el grupo Top-K es el mismo que el grupo de todos los expertos. Los kernels dispersos consumen los índices y puntajes Top-K directamente, mientras que los esquemas de despacho/combinación densos que dependen de all-to-all requieren que la información de enrutamiento se agregue por experto en lugar de por token.
Trabajo futuro
En futuros trabajos, planeamos optimizar aún más el rendimiento del modelo DeepSeek.
La siguiente optimización más importante es la desagregación de prellenado (Prefill Disaggregation). Las fases de prellenado (Prefill) y decodificación (Decode) del modelo DeepSeek-V3/R1 tienen características computacionales muy diferentes. Ambas pueden utilizar diferentes estrategias de optimización y esquemas de implementación.
Para la capa MLA, en la fase de decodificación, utilizamos la absorción de matrices para reducir el recuento de FLOP del cómputo de MLA. En la fase de prellenado, proyectar primero el vector latente en el espacio K/V y luego calcular en forma de atención multi-head (MHA) funcionaría mejor.
Si el prellenado y la decodificación se ejecutan en la misma GPU, para reducir el impacto del prellenado en la velocidad de salida de la decodificación, normalmente utilizamos el prellenado fragmentado para dividir la consulta en múltiples fragmentos para el prellenado. Debido a que la caché KV almacena el vector latente, se vuelve difícil convertir MLA en forma de MHA.
Para la capa MoE, en la fase de decodificación, utilizamos un EP y un DP lo más grandes posible para aumentar el número de tokens de entrada por experto, mejorando así el rendimiento de GroupGEMM. En la fase de prellenado, debido a que el número de tokens ya es lo suficientemente grande, GroupGEMM ya está limitado por el cómputo. Por lo tanto, para el prellenado, podemos utilizar un EP y un DP más pequeños.
Si el prellenado y la decodificación se ejecutan en la misma GPU, siempre que cualquier grupo de DP esté realizando el prellenado, la latencia de las capas MoE en todas las GPUs aumentará, afectando significativamente la velocidad de salida de la decodificación.
Además de la desagregación de prellenado, también planeamos optimizar los siguientes aspectos:
Rendimiento de AllToAll: nuestro kernel AllToAll actualmente solo puede alcanzar 1/3 del ancho de banda de InfiniBand. Continuaremos optimizando este kernel.
Decodificación especulativa al estilo EAGLE: en los datos anteriores, asumimos el uso de la decodificación especulativa para predecir 1 token. EAGLE puede utilizar una estructura de árbol para predecir múltiples tokens, mejorando la longitud de aceptación, lo que puede aumentar significativamente la velocidad de salida.
Kernel GEMM: en el modelo Roofline mostrado anteriormente, podemos ver que la eficiencia del kernel GEMM aún está lejos del límite teórico. Continuaremos optimizando este kernel.
GB200 NVL72: en la última solución GB200 NVL72 de NVIDIA, 72 GPUs Blackwell están interconectadas a través de NVLink de alta velocidad. Para los modelos con arquitectura MoE, esto representa una gran oportunidad y un gran desafío.
Conclusión
La implementación multinodo de los modelos DeepSeek MoE logra lo que normalmente es imposible con los LLM densos: mejorar simultáneamente tanto el rendimiento como la latencia. Al distribuir los expertos en más GPUs, reducimos la presión sobre el ancho de banda de la memoria por dispositivo, lo que permite un procesamiento más rápido y un mayor rendimiento del sistema. Nuestros experimentos muestran que las configuraciones EP128 logran un rendimiento hasta 5 veces mayor a velocidades de salida equivalentes en comparación con las implementaciones en un solo nodo.
Las técnicas de solapamiento de cómputo y comunicación, como los microlotes, reducen significativamente la sobrecarga de comunicación multinodo, y nuestra implementación muestra una aceleración de hasta el 40 %. Nuestros kernels de comunicación AllToAll personalizados y las implementaciones de kernels optimizados han permitido la implementación eficiente del modelo de 671 mil millones de parámetros.
A medida que las arquitecturas MoE ganan popularidad por su capacidad, estas estrategias de implementación proporcionan información valiosa para escalar dichos modelos de manera eficiente.
Referencias
Informe técnico de DeepSeek-V3
Descripción general del sistema de inferencia DeepSeek-V3/R1
EAGLE: el muestreo especulativo requiere repensar la incertidumbre de las características
Citación
@online{perplexity2025LowerLatencyAndHigherThroughputWithMultiNodeDeepseekDeployment,
author = {{Perplexity} and Lequn Chen and Nandor Licker and Erin McKnight},
title = {{Lower latency and higher throughput with multi-node DeepSeek deployment}},
date = {2025-04-18},
url = {https://perplexity.coolkeypoint.com/hub/blog/lower-latency-and-higher-throughput-with-multi-node-deepseek-deployment}
}