Article

Latence réduite et débit accru grâce au déploiement Multi-Node DeepSeek

Baleine lumineuse, faisant référence à DeepSeek

Dans la plupart des systèmes, la latence et le débit sont souvent des objectifs conflictuels nécessitant des compromis lors de la conception et du déploiement. Par exemple, dans les modèles de langage dense, l'augmentation de la taille du lot peut améliorer le débit mais augmente également la latence ; augmenter le parallélisme des tenseurs au sein d'une seule machine peut réduire la latence mais diminue le nombre de répliques, entraînant un débit plus faible.

Les modèles Mixture of Experts (MoE) comme DeepSeek-V3/R1 ont récemment démontré d'excellentes capacités de modèle et une efficacité opérationnelle. Par exemple, le modèle DeepSeek-V3/R1 comporte au total 671 milliards de paramètres, mais chaque jeton utilise seulement 37 milliards de paramètres lors de l'inférence. Cette architecture de modèle présente à la fois des défis et des opportunités pour les systèmes d'inférence.

Cet article démontre que, contrairement aux systèmes conventionnels, les modèles MoE comme DeepSeek-V3/R1 peuvent atteindre simultanément un débit plus élevé et une latence plus faible lorsqu'ils utilisent plus de GPU dans des déploiements multi-nœuds dans la plupart des scénarios.


Architectures de Déploiement

En raison du grand nombre de petits experts que le modèle possède, les déploiements doivent être répartis sur plusieurs appareils. Nous avons considéré à la fois les déploiements sur un seul nœud avec un seul nœud de 8xH200 GPU et des déploiements multi-nœuds sur 8xH100 GPU.

Les deux architectures de déploiement tirent parti du parallélisme de données, orchestré via notre planificateur de requêtes interne. L'implémentation du parallélisme de données consiste à lancer plusieurs instances de moteur d'inférence, chacune opérant de manière indépendante pour servir et gérer les requêtes. Le planificateur de requêtes, qui interagit avec le moteur via GRPC, est responsable de la répartition des requêtes aussi équitablement que possible, tout en facilitant la réutilisation de KV, en envoyant les requêtes avec préfixe partiellement correspondant aux serveurs contenant le cache. Les instances de moteur ne s'étalent pas sur plusieurs nœuds. Elles peuvent utiliser en option le parallélisme des tenseurs pour répartir l'attention sur plusieurs appareils. Les instances sont interconnectées via NVLink dans le cas à un seul nœud ou InfiniBand pour le cas multi-nœuds, distribuant et collectant les experts.

La configuration de déploiement sur un seul nœud offre une latence supérieure avec de petites tailles de lots ; cependant, les performances se détériorent rapidement sous des conditions de charge accrue.

Pour déployer le moteur de service, nous lançons un pod par nœud hébergeant plusieurs instances de moteur. PyTorch est responsable de la configuration de la communication distribuée et de la négociation de l'initialisation de NVSHMEM. Pour la communication, nous nous appuyons sur des noyaux CUDA personnalisés décrits dans un article de blog antérieur. L'implémentation des deux déploiements est pratiquement identique, le modèle choisissant les bons noyaux à utiliser en fonction du tissu implémentant le parallélisme des experts.


Techniques de Parallélisation

Avant de plonger dans nos comparaisons de performances, il est essentiel de comprendre les principales stratégies de parallélisation qui rendent possible le déploiement de modèles MoE massifs comme DeepSeek-V3/R1.

Parallélisme des Tenseurs

Dans l'inférence LLM, le parallélisme des tenseurs (TP) est généralement utilisé pour réduire l'utilisation de la mémoire et les calculs par GPU, réduisant ainsi la latence. Habituellement, nous pouvons fragmenter les projections linéaires dans les couches d'attention et les couches MLP le long des dimensions de ligne ou de colonne, et fragmenter les opérations d'attention le long de la dimension de la tête d'attention.

Avec TP, l'architecture Llama-3 n'a pas de calcul dupliqué pour les opérations de projection linéaire et d'attention à travers les GPU, ce qui est une méthode de fragmentation idéale. Cependant, dans les modèles DeepSeek-V3/R1, TP ne peut pas y parvenir.

Les modèles DeepSeek-V3/R1 utilisent une attention multi-latente (MLA). Une couche MLA utilise d'abord une projection linéaire kv_a_proj pour calculer le vecteur latent, puis utilise une autre projection linéaire kv_b_proj pour le transformer dans l'espace de chaque tête d'attention. Étant donné que toutes les têtes d'attention partagent le même vecteur latent, TP ne peut pas fragmenter le vecteur latent, de sorte que tous les rangs TP doivent répliquer les paramètres et le calcul de kv_a_proj et kv_b_proj. De même, puisque MLA stocke le vecteur latent dans le cache KV, chaque rang TP stocke une copie identique du cache KV.

Malgré une certaine duplication dans MLA, le parallélisme des tenseurs offre toujours une réduction partielle des exigences de calcul, le rendant précieux pour les scénarios nécessitant des vitesses de sortie élevées.

Parallélisme des Experts

Les modèles DeepSeek-V3/R1 remplacent les couches MLP par des couches MoE. Une couche MoE dispose de 256 experts routés et d'un expert partagé. Chaque jeton est envoyé à 8 experts routés différents pour le calcul, et les résultats sont additionnés avec pondération. Chaque jeton effectue également un calcul dans l'expert partagé, et le résultat est ajouté au résultat des experts routés.

Le parallélisme des experts (EP) sert de méthode de fragmentation typique pour les couches MoE, chaque GPU gérant 256 / EP experts routés tout en maintenant une copie de l'expert partagé. Comparé à TP, l'avantage de EP est qu'il peut distribuer le calcul sur davantage de GPU, réduisant ainsi l'utilisation de calcul et de mémoire par GPU.

Avant d'effectuer le calcul des experts, tous les GPU doivent effectuer une communication AllToAll pour envoyer les jetons vers les GPU où les experts correspondants se trouvent ; après le calcul des experts, une autre communication AllToAll est nécessaire pour collecter les résultats de calcul de divers GPU et effectuer une somme pondérée. Nous avons implémenté une version optimisée de ces deux noyaux de communication AllToAll, Dispatch et Combine, en utilisant NVSHMEM. Dans notre article de blog précédent, nous avons détaillé l'implémentation, et nos noyaux ont été open-sourcés sur GitHub.

Parallélisme de Données

Avec EP, nous pouvons distribuer le calcul MoE sur 128 GPU ou même plus. Cependant, le calcul MLA ne peut pas être partitionné avec EP. À ce stade, nous pouvons introduire le parallélisme de données (DP). Chaque groupe DP dispose d'une copie complète de la couche MLA. Chaque groupe DP accepte différentes entrées et effectue indépendamment le calcul de la couche MLA.

Le DP et le TP de la couche MLA peuvent être combinés, avec un groupe DP étant divisé en plusieurs rangs TP. Le EP de la couche MoE peut être combiné avec le DP/TP de la couche MLA. EP = DP * TP. Par exemple, sur 16 machines, EP128 DP32 TP4 signifie distribuer les experts routés sur 128 GPU, avec chaque 4 GPU formant un groupe DP, pour un total de 32 groupes DP indépendants.


Un seul nœud vs multi-nœuds

Les 671 milliards de paramètres de DeepSeek dépassent la capacité mémoire d'une seule machine à 8 GPU H100 (80 Go * 8), mais une seule machine à 8 GPU H200 peut accueillir entièrement l'ensemble du modèle (141 Go * 8). En utilisant la configuration EP8 DP8 TP1, le modèle utilise environ 100 Go de mémoire par GPU, laissant environ 40 Go pour le cache KV et d'autres résultats intermédiaires. Un jeton occupe 70 272 octets de cache KV. Supposant que chaque requête ait 5 000 jetons, chaque GPU peut accueillir environ 100 requêtes.

Nous voulions comprendre les différences de performances entre les déploiements à un seul nœud et multi-nœuds sous différentes configurations. Nous avons utilisé une machine H200 pour le déploiement à un seul nœud et jusqu'à 16 machines H100 pour les déploiements multi-nœuds. Pour chaque environnement de déploiement, nous avons utilisé des combinaisons de TP 1, 2, 4, 8, et des tailles de lot par GPU de 1, 2, 4, 8, 16, 32, 64, 128. Nous avons supposé que chaque requête avait une longueur de cache KV de 5 000 jetons. Nous avons également supposé que la prédiction multi-jeton (MTP) prédit 1 jeton supplémentaire (c'est-à-dire que la longueur de la requête de chaque requête est 2), et supposé de manière prudente un taux d'acceptation de 60%. La figure ci-dessous montre le débit et la vitesse de sortie pour différentes configurations.

L'axe horizontal représente la vitesse de sortie par requête en jetons/s. L'axe vertical utilise une échelle logarithmique pour montrer le débit par machine en jetons/s. Nous avons marqué la frontière de Pareto pour chaque configuration EP avec des lignes de différentes couleurs.

Dans les scénarios avec des exigences de vitesse de sortie extrêmement élevées, l'utilisation du mode EP8 DP1 TP8 à un seul nœud avec une taille de lot de 1 peut atteindre une vitesse de sortie supérieure à 100 jetons/s, mais le débit est extrêmement faible, équivalente à la vitesse de sortie. Dans ce scénario, le lot entier ne contient que 2 jetons, qui peuvent être envoyés à au plus 2*8=16 experts, activant un total de au plus 57 milliards de paramètres.

Dans la plage de vitesse de sortie de 80-40 jetons/s, à mesure que le débit augmente, la vitesse de sortie diminue considérablement. En revanche, EP128 a environ 5 fois plus de débit que le déploiement sur un nœud unique à la même vitesse de sortie.

Ce phénomène peut être expliqué en examinant le comportement des déploiements à un nœud unique : l'augmentation de la taille du lot est directement corrélée à une augmentation des experts activés. Lorsqu'un lot a une taille de 1, le nombre moyen d'experts activés par GPU est 2 * 8 / 8 = 2. Lorsque le lot est suffisamment grand, tous les experts sont activés, ce qui signifie que chaque GPU active 256 / 8 = 32 experts. Activer plus d'experts signifie que le GPU doit lire plus de paramètres depuis la mémoire, augmentant considérablement la pression sur la bande passante mémoire. Étant donné que la phase de décodage des grands modèles de langage est déjà limitée par la bande passante mémoire plutôt que par la performance de calcul, l'augmentation de la taille du lot dans le déploiement à un seul nœud réduit considérablement la vitesse de sortie.

La comparaison des quatre configurations multi-nœuds de déploiement (EP16, EP32, EP64, et EP128) révèle que des valeurs EP plus élevées déplacent la frontière de Pareto vers des améliorations simultanées du débit et de la vitesse de sortie.

L'utilisation d'un numéro EP plus élevé signifie que chaque GPU est alloué à moins d'experts. Par exemple, EP128 signifie que chaque GPU est responsable de 256 / 128 = 2 experts, de sorte que la pression sur la bande passante mémoire est considérablement réduite. En d'autres termes, en utilisant un nombre de EP plus grand, nous gagnons effectivement plus de bande passante mémoire. Lorsque la taille de lot par GPU est inférieure à 64, augmenter la taille de lot n'affecte pas significativement la vitesse de calcul des experts car l'augmentation du nombre d'entrées n'augmente pas significativement la pression sur la bande passante mémoire. Par conséquent, nous observons que lorsque nous utilisons EP128, augmenter la taille de lot n'affecte pas la vitesse de sortie aussi significativement.

Fait intéressant, sur des tailles de lots plus grandes (64 requêtes par GPU), nous avons observé un nouveau phénomène : le débit de déploiement sur un nœud unique est légèrement supérieur à celui du déploiement multi-nœuds. Une partie de la raison est que NVLink intra-nœud a une bande passante plus élevée que InfiniBand inter-nœud. Une autre partie est due aux limitations de notre implémentation. Nous analyserons ce phénomène plus en détail plus tard.

En raison des limitations de capacité de mémoire, la configuration EP8 DP8 TP1 ne peut pas atteindre une taille de lot de 128 par GPU, donc le déploiement multi-nœuds est toujours un meilleur choix dans les scénarios poursuivant un débit plus élevé.


Chevauchement du Calcul et de la Communication

Comme brièvement introduit ci-dessus concernant le parallélisme des experts, les GPU sont inactifs pendant la communication de la couche MoE. Pour réduire le gaspillage et diminuer la latence, nous devons trouver des tâches de calcul indépendantes des données pour remplir ce temps inactif.

La partie supérieure de la figure ci-dessus montre le flux de calcul d'une couche. Le calcul MoE dépend de Dispatch, et le calcul de la couche suivante dépend du résultat de Combine.

Nous plaçons l'expert partagé sur chaque GPU. De cette façon, le calcul de l'expert partagé ne nécessite pas de communication AllToAll. Par conséquent, nous pouvons effectuer le calcul de l'expert partagé immédiatement après l'envoi de Dispatch, puis attendre que la réception de Dispatch soit terminée. Nous appelons ce schéma de chevauchement "Dispatch Overlap".

Le chevauchement de Dispatch offre une implémentation simple et une applicabilité large. Cette technique cache le temps de calcul de l'expert partagé pour toutes les tailles EP et tailles de lot.

Pour augmenter davantage le chevauchement du calcul et de la communication, nous avons utilisé la microbatching mentionnée dans le rapport technique DeepSeek pour briser la dépendance des données. Comme indiqué dans la partie inférieure de la figure, nous avons divisé le calcul d'une couche Transformer en 5 étapes :

  • Étape 1 : Norme d'entrée, QKVProj, AppendKV, BMM

  • Étape 2 : BMM, Attn, OProj, PostNorm, Gate

  • Étape 3 : Envoi Dispatch, Expert Partagé

  • Étape 4 : Réception Dispatch, MoE, Envoi Combine

  • Étape 5 : Réception Combine

Dans les trois premières couches Transformer denses, nous utilisons tout le lot. Dans les 58 couches Transformer MoE suivantes, nous divisons uniformément le lot en deux microbatchs. Les deux microbatchs s'exécutent de manière alternée, décalés de 3 étapes. Comme il n'y a pas de dépendance de données entre ces deux microbatchs, nous pouvons passer au calcul de l'autre microbatch après l'envoi de Dispatch et après l'envoi de Combine.


Vérification de la Latence

Ensuite, nous comparons les effets du chevauchement par le biais d'une expérience, ainsi que les différences de performances entre le déploiement d'un seul nœud EP8 et le déploiement multi-nœuds EP128. Pour faciliter la comparaison, nous avons utilisé des GPU H100 pour l'expérience suivante. Nous avons utilisé TP1, une taille de lot de 128 par GPU, une longueur de requête de 2 par requête et une longueur de cache KV de 5000.

La figure ci-dessus montre le temps total passé sur une couche Transformer MoE et la proportion de latence de différents types de noyaux. À l'exception de Dispatch, Combine et GroupGEMM, le temps d'exécution des autres noyaux devrait être égal dans les séries EP8, EP128 NoOverlap et EP128 DispatchOverlap, car la taille de lot est la même.

Chevauchement

Comparons d'abord les effets des trois méthodes de chevauchement. NoOverlap a pris 2667µs au total, DispatchOverlap a pris 2651µs, économisant 16µs ou seulement 0,6%. Les microbatchs ont montré une amélioration très significative, prenant 1896µs, ce qui représente une accélération de 29%. Le temps de Dispatch et Combine a été considérablement réduit. Dispatch a diminué de 593µs à 367µs, et Combine de 1012µs à 237µs.

Notez que pour les noyaux de calcul, diviser un lot de taille 128 en deux lots de taille 64 augmente le temps d'exécution total. Par conséquent, bien que le temps passé en communication ait diminué de 1001µs, le temps total n'a été réduit que de 771µs. Nous expliquerons la raison en utilisant le modèle Roofline dans la section suivante.

Pour cette raison, le microbatching n'améliore pas toujours les performances.

La figure ci-dessus montre l'amélioration des performances du microbatching par rapport au DispatchOverlap pour des tailles de lot de 4 à 128. Lorsque la taille de lot est inférieure à 32, le microbatch réduit ses performances de 5 % à 40 %. Lorsque la taille de lot est supérieure ou égale à 32, le microbatch peut améliorer les performances de 10 % à 35 %.

EP8 vs EP128

Revenons à la figure précédente et comparons EP8 et EP128 Microbatch. EP8 a pris 1802µs au total, légèrement moins que les 1896µs d'EP128. Outre l'augmentation du temps d'exécution des noyaux entraînée par le microbatch mentionné ci-dessus, les principales différences résident dans le GroupGEMM utilisé pour le calcul MoE et les deux noyaux de communication, Dispatch et Combine.

Le GroupGEMM de EP8 a pris 555µs, tandis que celui de EP128 a pris 270µs, réduisant de moitié. C'est l'avantage central du déploiement multi-nœuds.

Malheureusement, le temps passé en communication a augmenté de 213µs, ce qui a considérablement compensé l'avantage de GroupGEMM. Dans des tests de performance distincts de nos noyaux de communication, nous avons constaté qu'ils ne peuvent atteindre que la moitié de la bande passante d'Infiniband. Nous continuerons d'optimiser nos noyaux de communication.

Un autre noyau qui prend sérieusement du retard est GEMM. Le microbatching a augmenté GEMM de 95µs. Nous analyserons GEMM plus en profondeur dans la section Roofline ci-dessous. Nous pensons que l'implémentation actuelle de GEMM n'a pas encore atteint des performances optimales.

Roofline

Le modèle Roofline est un bon outil pour analyser la performance des noyaux. Son axe horizontal est l'intensité arithmétique, le ratio des FLOP aux octets d'I/O mémoire. La valeur de l'axe horizontal peut être calculée directement à partir de la sémantique du noyau. L'axe vertical représente la performance atteinte, calculée en divisant les FLOP par la latence de référence.

La limite théorique supérieure de la performance des noyaux est déterminée directement par les spécifications du GPU. La performance FP8 maximale de l'H100 est de 1979 TFLOP/s, représentée comme une ligne horizontale dans le modèle Roofline. La bande passante mémoire de l'H100 est de 3,35 TB/s, représentée comme la pente d'une ligne passant par l'origine. Les deux lignes donnent les limites de performance pour les noyaux liés par les calculs et ceux liés par la mémoire, respectivement.

Ci-dessous, nous discutons de la performance des noyaux GroupGEMM et GEMM.

GroupGEMM

Le noyau GroupGEMM dans MoE effectue le calcul suivant : Il y a un total de g groupes, le i-ème groupe a m_i jetons, effectuant une multiplication matricielle de [m_i, k] x [k, n] -> [m_i, n]. Dans le test de performance, nous supposons que le nombre de jetons dans chaque groupe est le même, noté comme m_i = m. Ensuite, le compte FLOP pour GroupGEMM est 2 * g * m * k * n, et les octets d'I/O mémoire est g * (m * k + n * k + m * n).

Dans le modèle DeepSeek-V3/R1, il y a 256 experts, et chaque jeton est envoyé à 8 experts pour le calcul. En supposant une taille de lot de 128, une longueur de requête de 2, utilisant la configuration EP128 DP128, le nombre moyen de jetons reçus par chaque expert (c.-à-d. m) est 128 * 2 * 8 * 128 / 256 = 1024. De même, nous pouvons calculer m pour d'autres configurations et tailles de lot.

Nous avons utilisé l'implémentation GroupGEMM de DeepGEMM pour le test de performance. Les points de test ont couvert toutes les combinaisons de configurations EP8, EP16, EP32, EP64, EP128 avec TP1 et des tailles de lot de 1 à 128.

La figure ci-dessus montre le modèle Roofline pour GroupGEMM sous différentes configurations EP. Différents EP correspondent à un nombre différent de groupes. La figure illustre des lignes de performance quasi superposées, indiquant que la performance de GroupGEMM est principalement déterminée par le nombre total de jetons (représenté comme g * m).

Les étoiles marquent les points de données correspondant à une taille de lot de 128 par GPU pour chaque configuration EP. En comparant ces points de données étoilés, nous pouvons voir qu'à mesure que l'EP augmente (et que le DP augmente de manière synchrone), le nombre de jetons par expert m augmente également. À EP8, m=128, tandis qu'à EP128, m=2048.

À mesure que m augmente, l'intensité arithmétique augmente également. Dans la plupart des configurations, GroupGEMM est limité par la bande passante mémoire, donc augmenter m améliore la performance.

GEMM

Le noyau GEMM correspond aux projections linéaires dans le modèle, telles que les projections Q/K/V/O. Pour une multiplication matricielle de [m, k] x [k, n] -> [m, n], le compte FLOP est 2 * m * k * n, et les octets d'I/O mémoire est m * k + n * k + m * n. Nous pouvons également tester la latence pour des tailles de lot de 1 à 128.

La figure ci-dessus montre le modèle Roofline pour GEMM sous différentes configurations EP. Nous pouvons voir que la performance de GEMM est limitée par la bande passante mémoire. À mesure que la taille de lot augmente, l'intensité arithmétique augmente également, améliorant ainsi la performance.

Microbatch

Lorsque nous utilisons le microbatching, nous divisons le lot en deux parties égales. À partir des deux figures ci-dessus, nous pouvons voir que lorsque m devient m/2, l'efficacité de la multiplication matricielle diminue. Par conséquent, exécuter deux multiplications matricielles de taille m/2 prend plus de temps que d'exécuter une multiplication matricielle de taille m.

Prédiction Multi-Jetons

Tout au long de cet article, nous avons supposé l'utilisation de la prédiction multi-jetons (MTP) pour le décodage spéculatif. MTP change la longueur de la requête par demande de 1 à 2. Pour la multiplication matricielle, cela équivaut à changer m à m * 2, augmentant ainsi l'efficacité de la multiplication matricielle. D'autre part, si nous dessinions le modèle Roofline pour MLA, nous constaterions qu'augmenter la longueur de requête augmente significativement l'efficacité du noyau de MLA.

Par conséquent, l'utilisation de MTP joue un rôle important dans l'efficacité du modèle.

Implémentation & Optimisations

Dans cette section, nous introduirons certains détails d'implémentation et d'optimisation pour notre modèle DeepSeek-V3/R1.

Quantification

DeepSeek-V3/R1 a été entraîné nativement sur FP8 en utilisant un schéma de quantification par bloc, avec des poids quantifiés de manière statique et des activations quantifiées à la volée. Au lieu de calculer un facteur d'échelle par canal ou par matrice de manière statique, des facteurs d'échelle sont calculés sur des vecteurs de 128 éléments pour les activations et des blocs de 128x128 éléments pour les matrices, limitant la dégradation de précision due à la quantification.

Chez Perplexity, nous nous appuyons sur un mélange de noyaux CUDA et Triton pour soutenir l'inférence, avec CUDA utilisé pour les noyaux les plus sensibles à la performance et peu souvent modifiés (tels que l'attention et GEMM), avec Triton mettant en œuvre une large gamme de noyaux d'activation, de normalisation et utilitaires. Triton nous a permis d'adapter rapidement les noyaux au schéma de quantification par bloc.

Pour les couches linéaires et MoE, nous mélangeons les noyaux GEMM profonds avec nos propres noyaux GEMM Triton, car nous avons remarqué que pour certaines dimensions de matrices et de faibles tailles de lot, Split-K offre une latence inférieure. Si la couche non quantifiée effectue une multiplication (M, K) x (K, N), elle a besoin de (M x ceil_div(K, 128)) x (ceil_div(K, 128), ceil_div(N, 128)) comme facteur d'échelle pour la quantification par bloc. Pour la quantification par bloc, les facteurs d'échelle pour les activations sont calculés à la volée, plutôt que pré-étalonnés. Comme les facteurs d'échelle d'activation sont agrégés uniquement le long de la dimension K et non le long de la dimension M, les noyaux nécessitent seulement de légères altérations pour soutenir le schéma.

La fonction d'activation SiLU utilisée par DeepSeek-V3/R1 a nécessité des modifications substantielles pour soutenir les graphes CUDA, la quantification par bloc et les nombres de jetons acheminés dynamiquement. La quantification par bloc peut être problématique car elle introduit des réductions horizontales, cependant le noyau fragmentait déjà les activations le long de leur dimension cachée en blocs de 1024 éléments. À l'intérieur d'un bloc, le tenseur à quantifier était encore divisé en blocs de 128 pour calculer la plus grande valeur absolue, Triton générant des réductions maximales efficaces entre threads, ajoutant un surcoût minimal.

Pour soutenir l'acheminement MoE sous les graphes CUDA, les noyaux doivent être conscients des informations d'acheminement indiquant le nombre de jetons par expert, au lieu de planifier le travail en fonction de la taille des tampons qui ont été alloués pour contenir la limite supérieure des nombres de jetons. Nous ne pouvons pas diviser le problème en fonction des dimensions du tenseur d'entrée, donc nous lançons un nombre fixe de noyaux persistants qui lisent les informations d'acheminement pour déterminer combien de jetons sont peuplés et répartissent le travail de traitement des activations parmi eux de manière dynamique.

Nous avons déjà publié certains de nos noyaux pour le projet FlashInfer et dans le futur, nous ouvrirons davantage de notre code.

Couche MLA

Nous utilisons FlashInfer pour le calcul MLA. FlashInfer supporte des paramètres flexibles de table de pages et des performances extrêmement élevées.

Nous avons fusionné q_a_proj et kv_a_proj en un seul qkv_a_proj. La latence a diminué de 15,4 µs + 14,8 µs = 30,2 µs à 16,7 µs.

Nous avons décomposé kv_b_proj en deux matrices, k_b_proj et v_b_proj. Nous avons écrit un noyau BMM quantifié en blocs FP8 pour les calculs liés à ces deux matrices.

Graphique CUDA

Le graphique CUDA peut réduire considérablement le surcoût de lancement de noyau, ce qui est crucial pour les performances. Nous créons un graphique CUDA pour chaque taille de lot.

Avant de développer notre noyau AllToAll, nous utilisions torch.all_to_all_single() pour la communication AllToAll. Cette opération nécessite que tous les GPU utilisent la même taille de lot. Cependant, différents groupes DP peuvent utiliser différentes tailles de lot.

Pour assurer que all_to_all_single() est compatible avec différents groupes DP utilisant différentes tailles de lot, nous avons d'abord utilisé une opération allreduce() avant chaque exécution de modèle pour obtenir la taille de lot maximale parmi tous les groupes DP. Ensuite, nous faisions en sorte que tous les groupes DP utilisent cette taille de lot pour s'exécuter.

Bien que cette approche garantit que nous pouvons utiliser le graphique CUDA, elle présente trois inconvénients. Premièrement, elle nécessite une opération allreduce() supplémentaire. Deuxièmement, les groupes DP avec des tailles de lot plus petites sont forcés de se compléter. Troisièmement, elle rend notre code d'implémentation complexe.

Après avoir implémenté notre propre noyau AllToAll, nous ne nécessitons plus que tous les GPU utilisent la même taille de lot. Par conséquent, nous n'avons plus besoin d'effectuer des opérations allreduce() supplémentaires ou de compléter des tailles de lot.

Routeur MoE

Le routeur MoE est implémenté en Triton, s'appuyant sur un tri modifié dérivé de la bibliothèque standard qui suit également les indices des éléments triés. L'implémentation est partagée entre tous les modèles MoE, car l'acheminement Mixtral est un cas particulier des routes DeepSeek où le groupe Top-K est le même que le groupe de tous les experts. Les noyaux épars consomment directement les indices et scores Top-K, tandis que les schémas d'envoi/combine denses s'appuyant sur AllToAll nécessitent que les informations d'acheminement soient agrégées par expert au lieu d'une base par jeton.

Travail Futur

Dans les travaux futurs, nous prévoyons d'optimiser davantage les performances du modèle DeepSeek.

L'optimisation suivante la plus importante est la désagrégation de pré-remplissage. La phase de pré-remplissage et la phase de décodage du modèle DeepSeek-V3/R1 ont des caractéristiques computationnelles très différentes. Les deux peuvent utiliser différentes stratégies d'optimisation et schémas de déploiement.

Pour la couche MLA, dans la phase de décodage, nous utilisons l'absorption matricielle pour réduire le nombre de FLOP du calcul MLA. Dans la phase de pré-remplissage, projeter d'abord le vecteur latent dans l'espace K/V puis calculer sous forme d'attention multi-têtes (MHA) fonctionnerait mieux.

Si le pré-remplissage et le décodage s'exécutent sur le même GPU, pour réduire l'impact du pré-remplissage sur la vitesse de sortie de décodage, nous utilisons généralement le pré-remplissage par lots pour diviser la requête en plusieurs blocs pour le pré-remplissage. Étant donné que le cache KV stocke le vecteur latent, il devient difficile de convertir MLA en forme MHA.

Pour la couche MoE, dans la phase de décodage, nous utilisons autant de EP et DP que possible pour augmenter le nombre de jetons d'entrée par expert, améliorant ainsi la performance de GroupGEMM. Dans la phase de pré-remplissage, car le nombre de jetons est déjà suffisamment grand, GroupGEMM est déjà lié au calcul. Par conséquent, pour le pré-remplissage, nous pouvons utiliser des EP et DP plus petits.

Si le pré-remplissage et le décodage s'exécutent sur le même GPU, tant qu'un groupe DP effectue un pré-remplissage, la latence des couches MoE sur tous les GPU augmentera, affectant significativement la vitesse de sortie de décodage.

En plus de la désagrégation du pré-remplissage, nous prévoyons également d'optimiser les aspects suivants :

  • Performance AllToAll : Notre noyau AllToAll ne peut actuellement atteindre qu'un tiers de la bande passante d'Infiniband. Nous continuerons d'optimiser ce noyau.

  • Décodage spéculatif de style EAGLE : Dans les données ci-dessus, nous avons supposé l'utilisation du décodage spéculatif pour prédire 1 jeton. EAGLE peut utiliser une structure en arbre pour prédire plusieurs jetons, améliorant ainsi la longueur d'acceptation, ce qui peut augmenter significativement la vitesse de sortie.

  • Noyau GEMM : Dans le modèle Roofline montré plus tôt, nous pouvons voir que l'efficacité du noyau GEMM est encore loin de la limite théorique. Nous continuerons d'optimiser ce noyau.

  • GB200 NVL72 : Dans la dernière solution GB200 NVL72 de NVIDIA, 72 GPU Blackwell sont interconnectés via NVLink haute vitesse. Pour les modèles à architecture MoE, c'est une très grande opportunité et un défi.

Conclusion

Le déploiement multi-nœuds des modèles MoE DeepSeek réalise ce qui est généralement impossible avec les LLM denses : améliorer simultanément à la fois le débit et la latence. En distribuant les experts sur plus de GPU, nous réduisons la pression sur la bande passante mémoire par appareil, permettant un traitement plus rapide et un débit système plus élevé. Nos expériences montrent que les configurations EP128 atteignent jusqu'à 5 fois plus de débit à des vitesses de sortie équivalentes par rapport aux déploiements sur un seul nœud.

Des techniques de chevauchement calcul-communication comme le microbatching réduisent considérablement le surcoût de communication multi-nœuds, notre implémentation montrant jusqu'à 40 % d'accélération. Nos noyaux de communication AllToAll personnalisés et les implémentations de noyaux optimisés ont permis un déploiement efficace du modèle à 671 milliards de paramètres.

À mesure que les architectures MoE gagnent en popularité pour leur capacité, ces stratégies de déploiement fournissent des informations précieuses pour adapter efficacement de tels modèles.

Références

Intéressé par la façonner l'avenir de notre plateforme API ? Nous recrutons.

Rejoignez notre communauté de développeurs pour rester au courant des nouvelles versions, fonctionnalités et mises à jour.

Intéressé par la façonner l'avenir de notre plateforme API ? Nous recrutons.

Rejoignez notre communauté de développeurs pour rester au courant des nouvelles versions, fonctionnalités et mises à jour.

Intéressé par la façonner l'avenir de notre plateforme API ? Nous recrutons.

Rejoignez notre communauté de développeurs pour rester au courant des nouvelles versions, fonctionnalités et mises à jour.