vLLM BlogPublication de la source

Servir DeepSeek-V4.1-Flash à grande échelle : vLLM égale DeepInfer et passe à l'échelle sur GB300 NVL72

TL;DR : Sur un seul rack NVIDIA GB300 NVL72, vLLM avec la désagrégation P/D et Wide-EP sert DeepSeek-V4.1-Flash sur SemiAnalysis AgentX à 954K tokens/s par GPU avec un P90 de 196 tokens/s par utilisateur, 7.9× notre résultat sur un seul nœud du premier article et 38,96 $ par GPU-heure aux prix hors pointe de DeepSeek. Un chemin de décodage spécialisé atteint 826 tokens/s pour un seul utilisateur.

TL;DR : Sur un seul rack NVIDIA GB300 NVL72, vLLM avec la désagrégation P/D et Wide-EP sert DeepSeek-V4.1-Flash sur SemiAnalysis AgentX à 954K tokens/s par GPU avec un P90 de 196 tokens/s par utilisateur, 7.9× notre résultat sur un seul nœud du premier article et 38,96 $ par GPU-heure aux prix hors pointe de DeepSeek. Un chemin de décodage spécialisé atteint 826 tokens/s pour un seul utilisateur. Face à DeepInfer, le moteur propre à DeepSeek, le débit de décodage de vLLM se situe à moins de 6% sur les tailles de lot 8–256, et son prefill suit la courbe publiée par DeepInfer.

L'efficacité provient d'un travail d'ingénierie délibéré sur des composants que vLLM possède déjà, construit sur les kernels open-sourcés par DeepSeek. Cet article présente notre étude de performance, détaille chaque optimisation et partage les leçons que nous avons apprises :

  • Un moteur généraliste peut égaler un moteur spécifique à un modèle
  • Comprendre le modèle avant d'optimiser
  • De petites instances DEP fonctionnent mieux pour le prefill1
  • Le décodage Wide-EP passe bien à l'échelle avec EPLB
  • La surcharge CPU devient critique à l'échelle NVL72
  • Un chemin de décodage spécialisé débloque une faible latence

Vue d'ensemble des performances

Figure 1 : SemiAnalysis AgentX sur NVIDIA GB300 NVL72 : vLLM avec désagrégation P/D et Wide-EP (en or) obtient une augmentation supplémentaire de débit de 7.9× par rapport à notre résultat agrégé sur un seul nœud du premier article. Le débit compte les tokens d'entrée en cache, d'entrée hors cache et de sortie (source).

Comparaison avec DeepInfer

DeepInfer est le moteur d'inférence propre de DeepSeek pour DeepSeek-V4.1-Flash, publié cette semaine comme moteur hors ligne avec des courbes de prefill et de décodage publiées. C'est la référence la plus pertinente pour ce modèle, donc avant d'entrer dans nos optimisations, nous montrons où se situe vLLM par rapport à lui.

Décodage. Sous la configuration de décodage de DeepSeek (contexte de 131K, nextn=3), vLLM sur GB200 atteint un débit à moins de 6% des résultats publiés de DeepInfer sur les tailles de lot 8 à 256 (Figure 2).

Figure 2 : Débit de sortie en décodage par GPU en fonction de la taille de lot à 131K de contexte, nextn=3. vLLM sur GB200 vs. les résultats publiés par DeepInfer, lus à partir des graphiques de son README (source).

Préremplissage. La courbe de préremplissage publiée par DeepInfer utilise EP4DP4 avec 32 experts redondants et un replay borné de 140 tokens, sur un matériel que le graphique ne précise pas, et nous lisons ses valeurs sur le graphique. Notre exécution utilise DEP2 sur GB300 sans experts redondants, un replay de 128 tokens et nos propres prompts. Comme les configurations diffèrent, nous ne lisons pas l'écart entre les deux courbes comme un gain de vitesse d'un moteur à l'autre. Ce que montre la Figure 3, c'est que le préremplissage DEP2 de vLLM suit le moteur même de DeepSeek sur la plage qu'ils ont publiée.

Figure 3 : Débit de préremplissage en fonction des nouveaux tokens. vLLM DEP2 sur GB300 vs. la courbe publiée par DeepInfer (EP4DP4, 32 experts redondants, replay de 140 tokens, matériel non précisé), lu à partir du graphique de son README (source).

Les deux moteurs obtiennent des résultats proches car ils partagent une lignée dans les deux sens. DeepInfer a été construit à l'origine sur vLLM, et vLLM à son tour a continuellement intégré des kernels issus des bibliothèques de DeepSeek, notamment MegaAttn, MegaMoE et Mega-mHC, et a largement optimisé le reste de la pile autour de ceux-ci. DeepInfer cible un seul modèle en mode hors ligne ; la même version de vLLM sert également ce modèle en ligne avec désagrégation P/D et Wide-EP sur un rack NVL72 complet, ce que mesure le reste de cet article. Les sections qui suivent décrivent ce travail.

Sous le capot : d'où vient la vitesse

Mesurer d'abord le modèle

Avant tout réglage, nous avons commencé par quantifier les conceptions architecturales efficaces de DeepSeek-V4.1-Flash :

  • Faible empreinte du cache KV. Seulement 890 octets par token pour le cache KV global, grâce au partage KV inter-couches et à la compression FP4 (voir le DeepSeek-V4.1-Flash Technical Report).
  • Attention légère. Les couches 2, 8 et 14 utilisent une compression 2× pour l'indexation sur tout le contexte. Avec le replay borné, la couche 20 écrit le KV global pour tous les tokens mais n'effectue l'indexation et l'attention que sur les 128 derniers ; les couches 21–39 ne traitent que ces tokens.
  • Le décodage est limité par le MoE même en contexte long. Voir la Figure 5 ci-dessous. Avec DEP4 en décodage à 128K de contexte et 64 requêtes par rang, le MoE occupe 62% du step cible, tandis que l'attention et l'indexeur ne prennent ensemble que 13%.

Le tableau ci-dessous montre le temps de forward du modèle cible à DEP32, avec 16 requêtes par rang, 6 tokens par requête (un token plus cinq DSpark draft tokens), et toutes les requêtes à la même longueur de contexte, sur GB300. En passant de 128K à 855K, un contexte 6,7× plus long n'ajoute que 0,6 ms (+7%) à la latence de décodage du modèle cible.

Contexte par requête64K128K256K512K855K
Forward du modèle cible (ms)8.868.939.039.259.53
vs. 128K−0.8%–+1.1%+3.6%+6.7%

Ces caractéristiques façonnent le reste de notre stratégie d'optimisation :

  • Le prefill devrait s'exécuter à une petite taille DEP. Même une instance DEP2 sur B300 ou GB300 peut contenir à la fois les poids du modèle et le cache KV pour de nombreuses sessions concurrentes. Un world size plus petit simplifie grandement l'équilibrage de charge DP/EP.
  • La capacité KV limite rarement la taille de batch. Le cache KV tient confortablement dans la HBM, ce qui nous permet de pousser chaque opération vers ses limites de calcul ou de bande passante mémoire au lieu de contourner la capacité KV. Cela garde aussi notre balayage cohérent et peu bruité, sans préemption ni pression sur le cache KV.
  • Wide-EP est efficace pour le décodage. Le décodage MegaMoE est limité par les lectures de poids, et Wide-EP réduit directement ce coût MoE dominant.
  • L'équilibrage de charge DP pour le décodage est plus simple. Le temps d'étape de décodage de ce modèle varie peu avec la longueur du contexte, donc l'équilibrage de charge DP peut être simplifié en least-load par nombre de requêtes par rang.
  • Les charges de travail synthétiques restent représentatives pour le décodage. L'utilisation des longueurs moyennes d'entrée et de sortie nous donne une bonne estimation des performances sans rejouer la trace agentique complète.

Comment nous avons mesuré

Mesurer les performances avec précision sans compliquer excessivement la méthodologie n'est pas trivial. Nous le décomposons en deux questions : combien devrait coûter une étape de prefill ou de décodage, et quelle surcharge supplémentaire provient du déséquilibre DP et EP ?

  • Temps d'étape de prefill et de décodage. vllm bench forward (#61339) mesure le temps de model-forward pour un batch de prefill ou de décodage exact via le chemin execute_model réel à travers les rangs DP et EP. Cela mesure uniquement le temps GPU et fournit une trace propre. De plus, le DecodeBenchConnector de vLLM mesure les performances de décodage de bout en bout au niveau du moteur pour une concurrence donnée. L'écart entre les temps d'étape GPU uniquement et de service révèle des surcoûts inattendus.
  • Déséquilibre DP de prefill : replays mirror vs. lag. Les traces agentiques varient d'une exécution à l'autre, donc le déséquilibre DP est difficile à isoler. Nous rejouons donc des requêtes identiques sur chaque rang DP. Dans une exécution mirror , tous les rangs reçoivent leurs requêtes en même temps. Dans une exécution lag , le rang r reçoit les mêmes requêtes r × 5 s plus tard, de sorte que les rangs sont désynchronisés mais effectuent le même travail total. La différence entre les deux exécutions est le coût du déséquilibre DP.
  • Décoder le déséquilibre EP : routage réel vs uniforme. Pour chaque couche MoE, nous définissons l'équilibrage comme la charge moyenne des rangs divisée par la charge maximale des rangs, par étape et sur la fenêtre d'EPLB. Comme référence équilibrée, nous exécutons le même batch de décodage avec des tokens routés uniformément au hasard. Cette référence comporte tout de même un bruit d'échantillonnage : à DEP32 avec 72 requêtes par GPU, le routage uniforme atteint environ 0.96 d'équilibrage pour le target (routage top-6) et 0.94 pour le draft (top-3). L'écart entre le routage réel et le routage uniforme est le maximum que l'équilibrage des experts peut récupérer, et l'exécution avec EPLB montre quelle proportion de cet écart il récupère réellement.

Prefill : DEP2 fonctionne le mieux

Comme discuté ci-dessus, une petite taille DEP est intéressante pour le prefill. Dans une exécution AgentX en prefill seul à une concurrence de 256, DEP4 n'a atteint que 83% du débit par GPU de DEP2 (53.3K contre 64.4K tokens/s). Les rejeux mirror et lag avec le prefix caching désactivé pointent vers un déséquilibre DP : lorsque les rangs se désynchronisent, DEP4 perd 13.6% de son débit.2 Cela fait de DEP2 un choix par défaut simple pour nous.

Débit de prefill par instance (tokens/s)mirror (équilibré)lag (déséquilibré)perte
DEP2138.7K130.5K5.9%
DEP4267.6K231.1K13.6%

Optimisations cachées derrière la trace

Notre premier article couvrait un large ensemble d'optimisations de préfill. Des traces plus propres nous ont aidés à identifier les goulots d'étranglement restants, en particulier ceux qui croissent avec la longueur du contexte. Par exemple, sur deux GPU GB300, un préfill de 8K tokens prend 87.9 ms sans contexte préalable, mais 189.4 ms avec 512K de contexte en cache. Nous avons traité trois goulots d'étranglement supplémentaires :

Étendre le replay borné SWA à la couche 20 (#59894) : +19.9% à 32K nouveaux tokens

Le replay borné SWA limite déjà les couches 21–39 aux 128 derniers tokens. Auparavant, la couche 20 effectuait encore le calcul d'indexeur sur tous les nouveaux tokens. Désormais, elle écrit le KV pour tous les nouveaux tokens mais n'exécute le reste de ses calculs que sur le lot de replay de 128 tokens. Cela améliore le débit de préfill de 19.9% à 32K nouveaux tokens, avec des gains allant de 10.1% à 4K à 23.1% à 64K.

Utiliser des pages de 2 Mo pour les tables hôtes partagées d'Engram (#62359) : encore +2.8% à 32K

Engram effectue des lectures aléatoires depuis des tables hôtes partagées entre les rangs DP. Le /dev/shm mappage précédent utilisait des pages GPU de 64 Ko, provoquant de fréquents défauts de TLB GPU. Le déplacement des tables vers des allocations cuMem host-NUMA permet des pages GPU de 2 Mo, améliorant encore le débit de préfill de 2.8% à 32K nouveaux tokens.

Utiliser le top-k DeepSelect dans l'indexeur de préfill (#62240) : encore +15.6% à 32K

L'indexeur de préfill utilise désormais DeepSelect pour classer les blocs candidats, à l'image du chemin de décodage. Son bénéfice croît avec le nombre de clés classées, améliorant encore le débit de préfill de 15.6% à 32K nouveaux tokens et 15.2% à 64K.

Ensemble, ces changements améliorent le débit de préfill des nouveaux tokens de 35.6–43.9% sur 4K–64K nouveaux tokens par rapport à un préfixe en cache de 128K. À 32K, le gain combiné est de 42.5%; à 64K, le débit passe de 71.2K à 102.5K tokens/s/GPU (+43.9%).

Figure 4 : Débit de préfill des nouveaux tokens par GPU sur DEP2 après un hit de cache de 128K, en ajoutant un par un le replay de la couche 20, les pages Engram de 2 Mo et le top-k DeepSelect.

Décode : mise à l'échelle du Wide-EP et équilibrage des experts avec EPLB

Côté décode, le Wide-EP avec EPLB est une pratique établie, décrite dans la présentation du système d'inférence de DeepSeek. vLLM prend en charge les deux depuis un certain temps (voir notre article sur le service à grande échelle), et nous avons constaté qu'ils fonctionnent toujours bien. Cette section se concentre sur nos nouvelles découvertes et leçons empiriques.

La mise à l'échelle de Wide-EP

Nous avons mesuré la mise à l'échelle du décodage de DEP4 à DEP32 avec 64 requêtes par GPU à un contexte de 128K (avec EPLB activé ; voir la section suivante). Le temps MegaMoE cible passe de 13.4 à 6.0 ms à mesure que chaque rang détient moins d'experts, tandis que le travail restant se maintient autour de 10 ms. Par conséquent, les gains de débit par GPU diminuent progressivement. À DEP32, la surcharge de communication du draft commence également à compenser les avantages d'un parallélisme plus large.

Figure 5 : Temps d'étape de décodage par rang de DEP4 à DEP32 sur NVIDIA GB200 avec 64 requêtes par GPU à un contexte de 128K, et débit de sortie par GPU.

EPLB

Nous laissons EPLB réorganiser les experts tous les 3,000 pas, en fonction de la charge des 1,000 pas précédents. EPLB fait passer l'équilibre de 0.35 à 0.94 pour le modèle cible et de 0.21 à 0.89 pour le draft, récupérant environ 97% et 93% de l'écart par rapport au routage uniforme.

Figure 6 : Décodage sur GB300 NVL72 avec EPLB désactivé, EPLB pour le modèle cible uniquement, et EPLB pour le modèle cible et le draft DSpark. Les onglets montrent l'équilibre par étape, la répartition du temps par étape à DEP32, et le débit (moyenne de trois exécutions ; les barres indiquent la plage).

Deux changements rendent cela possible. #62009 introduit un backend basé sur NVLink symm_mem qui rééquilibre l'ensemble de DEP32 en moins d'une seconde sans arrêter le décodage, fusionne la réplique et l'enregistrement de charge d'EPLB dans les kernels MegaMoE (économisant environ 0.2 ms par étape), et supprime la synchronisation hôte de la boucle de décodage. #61395 prend en charge EPLB avec des modèles cibles et drafts hétérogènes. Le draft DSpark a 128 experts routés avec un routage top-3, contre 384 avec top-6 pour le modèle cible, donc EPLB suit séparément le placement des experts et la charge pour chaque modèle et rééquilibre les deux.

Surmonter la surcharge CPU dans le décodage rapide à grande échelle

Le décodage Wide-EP synchronise tous les rangs à chaque couche, de sorte qu'un rang lent bloque l'instance. La planification asynchrone masque bien la surcharge CPU à petite échelle, mais un plus grand nombre de rangs rend cela plus difficile de deux manières : les étapes GPU deviennent plus courtes, ce qui laisse moins de marge pour le travail CPU, et il devient plus probable qu'un rang prenne du retard.

En pratique, nous avons observé qu'un DEP32 non optimisé s'exécutait plus lentement que le DEP16 (17.57 contre 13.84 ms par étape), bien que son exécution GPU était 1.1 ms plus rapide. Le tableau ci-dessous répertorie toutes les causes que nous avons trouvées, classées par effet. Les effets proviennent de différentes configurations, donc chaque ligne indique la sienne.

Afficher les causes, les correctifs et leurs effets
CauseCorrectifPRsEffetConfiguration
Lecture KV postée de manière synchrone sur le thread de travail de décodageNIXL push avec UCX scatter-gather ; lignes KV packagées envoyées en une seule région NIXLConnecteur push NIXL, #61121141K → 440K tokens/s par GPU ; P90 ~40 → 159 tokens/s par utilisateurGB300, 8P1D, concurrence 3,072
La latence all-reduce de Gloo croît linéairement avec le nombre de rangsAll-reduce CPU par doublement récursif#61767, en s'appuyant sur #60267Sync DP 10,8 → 1,5 ms par pas ; pas 16,10 → 13,15 msDécodage DEP32
Rafales d'admission : jusqu'à 140 requêtes admises en un pasPlafond optionnel de 4 nouveaux chargements KV par pas#61725460K → 516K tokens/s par GPU ; P90 109 → 153 tokens/s par utilisateurGB300, 7P1D, concurrence 4,800, avec deux autres correctifs CPU
Travail d'admission qui croît avec la longueur du promptPrompt en un seul tampon int32 ; saut des blocs null des tables de blocs à fenêtre glissante ; noyau de copie du prompt par blocs#59908, #62702, #61724, #62698Prompt de 855K : 38,8 → 6,0 ms de CPU par admissionMicrobenchmark du moteur

Faible latence : un chemin de décodage spécialisé pour les petits lots

Nous avons également étendu la prise en charge des noyaux basse latence à DeepSeek-V4.1-Flash (#61868 et #61869), poursuivant les efforts de notre previous post. Sur de vraies sessions AgentX, le chemin de décodage spécialisé atteint 826 tokens/s en P90 pour un seul utilisateur sur quatre GPU B300, soit 44 % de plus que le chemin de décodage standard (575).

Figure 7 : SemiAnalysis AgentX avec TP4 sur quatre GPU B300 à une concurrence de 1, 8 et 16, avant et après InferenceX #4332 soit passé au chemin de décodage spécialisé.

Remerciements

Nous tenons à remercier DeepSeek d'avoir rendu DeepSeek-V4.1-Flash et ses noyaux optimisés open source et de les avoir partagés, ce qui a fourni la base de ce travail, ainsi que d'avoir publié DeepInfer et ses résultats de performance, qui nous ont donné une référence solide pour apprendre et comparer. Nous remercions également SemiAnalysis pour la plateforme de benchmark AgentX et toute l'aide apportée.

Nous sommes particulièrement reconnaissants envers Pei Li, Hanjie Qiu, Lisa Wang, Yaoyao Ding, Rohit Nagraj et Xin Li de NVIDIA pour leur étroite collaboration sur l'optimisation des performances, leur soutien continu à travers vLLM et Dynamo, et leur aide pour les soumissions AgentX. Nous remercions également Canlin Guo de la communauté vLLM pour sa précieuse optimisation du prefill.

Ce travail a été dirigé par Inferact.

Notes de bas de page

  1. DEPn désigne le parallélisme de données pour l'attention plus le parallélisme d'experts sur n GPUs. Le forward cible est la passe forward complète du modèle principal à chaque étape de décodage, qui vérifie les tokens draft et produit la sortie ; le forward draft est la passe forward complète du modèle draft DSpark, qui propose les tokens pour l'étape suivante. ↩

  2. Mesuré sur une version antérieure ; le coût du déséquilibre est une propriété de la taille DEP, et non des kernels. ↩

Source originale

vLLM Blog

À propos du contenu

La publication originale et les droits appartiennent à la source.

Traduction automatique · Consultez l’original