Des articles approfondis sur les technologies qui façonnent l'avenir.

Le moteur d'inférence de DeepSeek : 5 mythes, testés

DeepSeek a publié en open source son moteur d'inférence LLM de production. Nous confrontons cinq idées reçues à ce que DeepInfer révèle vraiment.

Illustration de jetons circulant sur des tapis roulants vers des machines GPU, représentant l'inférence LLM par lots
DeepInfer est la pile de service que DeepSeek fait réellement tourner, ce qui en fait une fenêtre rare sur l'inférence à l'échelle de la production.

Quand DeepSeek a publié son moteur d'inférence LLM de production sur GitHub sous licence Apache 2.0, le dépôt a dépassé les milliers d'étoiles en quelques heures. Rien d'étonnant à cela. Ce qui est surprenant, c'est ce qu'il y a dedans. DeepInfer n'est ni un jouet de recherche ni une démo allégée. C'est le moteur qui, d'après DeepSeek lui-même, sert DeepSeek-V4.1-Flash à une échelle annoncée de 155 000 milliards de jetons par jour et par cluster, avec un revenu d'environ 38 dollars par heure GPU en tarif heures creuses. Ces chiffres, s'ils sont même à peu près exacts, reposent sur des choix d'ingénierie que la plupart de l'industrie considère comme du folklore. Alors faisons ce que je fais avec tout système de production fraîchement ouvert : lisons le README en sceptique et testons les idées reçues sur les moteurs d'inférence à la lumière de ce document.

Mythe 1 : un moteur d'inférence, ce n'est qu'une passe avant rapide

Le modèle mental naïf ressemble à ceci : le modèle, c'est le modèle ; le moteur n'est qu'une fine enveloppe qui appelle les kernels ; et la performance vient de l'achat de GPU plus rapides. La liste des fonctionnalités de DeepInfer démolit cette vision. Regardez ce qu'ils jugent digne d'être livré : une ordonnancement asynchrone avec un « planificateur de lots sans surcoût », du décodage spéculatif avec vérification planifiée selon la confiance, une réinjection bornée pour l'attention à fenêtre glissante, une inférence invariante au batch, un échantillonnage déterministe, et des sorties structurées contraintes par des expressions régulières, des schémas JSON ou des grammaires EBNF. Cela fait un ordonnanceur, un gestionnaire de mémoire, un système de vérification et un compilateur de contraintes. Les kernels arrivent presque en fin de liste, comme une réflexion après coup.

C'est ce que les praticiens réapprennent sans cesse. La passe avant est la partie facile : elle est pratiquement résolue depuis FlashAttention et les bonnes bibliothèques GEMM. Le plus dur, c'est de garder le GPU alimenté. Chaque milliseconde pendant laquelle les SM restent inactifs pendant que le CPU décide quelles requêtes admettre, quels blocs de KV évincer ou quels jetons de brouillon vérifier, c'est de l'argent qui s'évapore. Le planificateur asynchrone de DeepInfer existe précisément pour chevaucher la planification CPU et l'exécution sur le périphérique, pour que le GPU n'attende jamais Python. Si votre pile de service planifie encore de façon synchrone, ce seul changement vaut probablement plus que n'importe quel remplacement de kernel que vous aviez prévu.

Le README contient aussi un aveu révélateur : un service d'inférence complet a en plus besoin d'un système de cache KV, d'une séparation prefill/decode et d'un équilibrage de charge entre réplicas à parallélisme de données, et rien de tout cela n'est inclus. Autrement dit, même le moteur n'est qu'une couche du système réel. L'inférence en production est un problème de systèmes distribués déguisé en problème de machine learning. Nous l'avions déjà souligné à propos de ces agents LLM qui ressemblent beaucoup à des systèmes distribués, et c'est tout aussi vrai une couche plus bas dans la pile.

Mythe 2 : des batchs plus gros, c'est toujours mieux

Le batching continu, qui consiste à admettre de nouvelles requêtes dans le batch dès que d'autres se terminent plutôt que d'attendre la fin de tout le batch, est aujourd'hui un minimum, et DeepInfer le fait évidemment. Mais la croyance la plus intéressante à tester est la version approximative : « le débit croît avec la taille du batch, donc maximisons le batch ». DeepInfer embarque deux fonctionnalités qui existent précisément parce que c'est faux.

D'abord, l'inférence invariante au batch (DEEPINFER_BATCH_INVARIANT=1). Normalement, la sortie d'une requête peut varier selon ce qui se trouve dans son batch, car l'ordre des réductions dans les kernels change avec la composition du batch, et l'addition en virgule flottante n'est pas associative. DeepSeek a jugé qu'il valait l'effort d'ingénierie de rendre les résultats identiques quelle que soit la composition du batch. Pourquoi ? Parce qu'une fois qu'on sert à grande échelle, on constate que les clients et les pipelines d'évaluation remarquent quand le même prompt renvoie des jetons différents d'un jour à l'autre. Si maximiser la taille du batch impliquait d'accepter du non-déterminisme, DeepSeek a choisi de sacrifier du débit pour retrouver du déterminisme. Cela montre que la fonction objectif réelle n'est pas le nombre brut de jetons par seconde, mais les jetons par seconde dignes de confiance.

Ensuite, la vérification planifiée selon la confiance pour le décodage spéculatif. Le moteur produit des jetons de brouillon (longueur maximale de brouillon par défaut : 5) et utilise des estimations de confiance pour décider de la quantité de travail de vérification à planifier. L'approche naïve vérifie un brouillon de longueur fixe à chaque étape ; l'approche intelligente reconnaît que la quantité optimale de travail spéculatif varie selon la requête et selon l'étape. Mettre dans le batch plus de travail spéculatif que la confiance ne le justifie gaspille du calcul sur des jetons qu'on jettera. En mettre moins laisse de l'accélération sur la table. L'ordonnanceur résout donc un problème d'optimisation à chaque étape, au lieu de simplement entasser les requêtes les unes à côté des autres.

  • La taille du batch augmente le débit mais aussi la latence : le temps jusqu'au premier jeton se dégrade à mesure que les batchs de prefill grossissent.
  • La composition du batch modifie la numérique : sans payer pour des kernels invariants au batch, les sorties dérivent.
  • Le travail spéculatif a des rendements décroissants : le taux d'acceptation des brouillons baisse avec leur longueur, donc les calendriers fixes gaspillent des FLOPs.
  • Le cache KV est le vrai plafond : le batch ne peut pas grandir au-delà de ce que la mémoire peut contenir, c'est pourquoi la disposition mémoire compte plus que la politique de batch.

Mythe 3 : la gestion du cache KV est un problème résolu

PagedAttention a popularisé l'approche inspirée des systèmes d'exploitation : allouer le cache KV en pages de taille fixe, les distribuer comme de la mémoire virtuelle et cesser de fragmenter. vLLM a prouvé que ça marche, et DeepInfer, selon ses propres remerciements, était à l'origine principalement construit sur vLLM. Donc le cache KV paginé doit être réglé, non ? Pas tout à fait. Le README de DeepInfer mentionne avec désinvolture quelque chose de bien plus agressif : l'un des objectifs de conception du système de production était de conserver le cache KV pendant plus de 72 heures.

Illustration isométrique d'une hiérarchie mémoire à plusieurs niveaux, avec des blocs de cache déplacés entre des puces rapides et un stockage profond
Un cache KV de 72 heures suppose une hiérarchie mémoire à plusieurs niveaux : la HBM n'est que l'étagère du haut.

Réfléchissez à ce que cela implique. La HBM, même sur les plus gros GPU, se mesure en centaines de gigaoctets ; un cache de 72 heures sur un cluster chargé représente des pétaoctets d'état. Impossible de tout garder sur le périphérique. Il faut un système KV à plusieurs niveaux (HBM, DRAM de l'hôte, NVMe, éventuellement stockage distant), avec des politiques d'éviction et de préchargement assez intelligentes pour que le rappel d'un préfixe ancien ne ruine pas votre SLO de latence. C'est pourquoi le README liste « un système de cache KV » parmi les éléments non inclus dans la publication : c'est une infrastructure à part entière, et sérieuse. Si vous traitez encore le cache KV comme un problème de pagination dans le GPU, la frontière s'est déplacée vers un traitement en tant que système de stockage. Nous avons abordé la version à plus petite échelle de cette difficulté dans ce qu'il faut faire quand votre GPU manque de VRAM, mais là, on parle de ce problème à l'échelle d'un datacenter.

Il y a aussi une astuce KV plus subtile à piquer : la réinjection bornée SWA. Pour les couches d'attention à fenêtre glissante du décodeur, il n'est pas nécessaire de rejouer tout le prompt pendant le prefill, seulement un suffixe borné qui couvre la fenêtre d'attention plus la longueur maximale de retour en arrière (par défaut : conserver 140 jetons). C'est une réduction directe du calcul de prefill pour les longs contextes, et le genre d'optimisation qu'on ne découvre qu'en profilant l'endroit où partent réellement les FLOPs du prefill. Les moteurs génériques ne peuvent pas le faire en toute sécurité : il faut connaître intimement l'architecture d'attention du modèle. Une fois de plus, les moteurs spécifiques à un modèle gagnent.

Mythe 4 : le décodage spéculatif, c'est du débit gratuit

Le décodage spéculatif a un argument presque magique : générer plusieurs jetons par passe avant grâce à un brouillon bon marché, les vérifier en parallèle et garder ceux qui correspondent. Les benchmarks en raffolent. Mais la croyance selon laquelle c'est « gratuit » s'effondre dès qu'on lit comment DeepInfer le déploie réellement. Les drapeaux parlent d'eux-mêmes : DEEPINFER_ENABLE_MTP_VARLEN=1 pour l'activer, DEEPINFER_USE_NEXTN_DECODE pour plafonner la longueur du brouillon, et une couche de planification par confiance par-dessus. Si c'était gratuit, vous n'auriez besoin de rien de tout cela : vous rédigeriez toujours un brouillon.

L'économie fonctionne ainsi. Rédiger et vérifier un brouillon coûte des FLOPs supplémentaires à chaque étape. On ne gagne que si le taux d'acceptation est assez élevé pour que le gain en jetons par étape dépasse le surcoût. Ce taux dépend du mécanisme de brouillon, du domaine (le code se prête très bien au brouillon, la prose bavarde beaucoup moins) et, point crucial, de la charge du batch. Avec de grosses tailles de batch, le GPU est déjà limité par le calcul : les FLOPs de vérification supplémentaires évincent directement le travail utile. Le décodage spéculatif brille dans le régime limité par la latence et peut réellement nuire dans le régime limité par le débit. Un moteur de production qui sert les deux régimes doit le planifier de façon adaptative, et c'est exactement ce que fait la vérification planifiée selon la confiance.

Mon avis, après avoir vu des équipes greffer le décodage spéculatif sur leurs piles de service : traitez-le comme une fonctionnalité de latence, pas de débit. Si votre goulot d'étranglement, c'est le nombre de jetons par dollar à forte utilisation, corrigez d'abord votre ordonnanceur et la disposition de votre KV. Si c'est la latence p99 pour des utilisateurs interactifs à charge modérée, le décodage spéculatif est l'un des meilleurs outils disponibles, et DeepInfer offre une référence éprouvée pour savoir comment le conditionner.

Mythe 5 : il faut la bénédiction de NVIDIA (et rien d'autre) pour servir à grande échelle

DeepInfer prend en charge exactement deux cibles matérielles : les GPU NVIDIA SM100 et les NPU Huawei Ascend 950. Il maintient des fichiers de dépendances séparés par plateforme (requirements-nvidia.txt, requirements-ascend.txt) et active les fonctionnalités en conséquence : l'inférence invariante au batch est réservée à NVIDIA, par exemple. C'est une déclaration discrète mais importante. Un moteur de production haut de gamme traite un accélérateur non NVIDIA comme un citoyen de première classe, avec le même modèle, le même ordonnanceur et la même architecture de service.

Deux routes sur un circuit imprimé qui divergent vers des horizons de couleurs différentes, symbolisant deux écosystèmes GPU
Un support de première classe pour deux familles d'accélérateurs dans un même moteur affaiblit l'idée qu'une inférence sérieuse exige un seul fournisseur.

Soyons clairs sur ce que cela veut dire et ne veut pas dire : cela ne signifie pas que la voie Ascend est mature, et cela ne signifie pas que le fossé de CUDA s'évapore du jour au lendemain. Les fonctionnalités réservées à CUDA dans le même README prouvent que l'écart de l'écosystème est bien réel. Ce que cela signifie, c'est que le problème logiciel le plus difficile de la portabilité de l'inférence (le moteur, l'ordonnanceur, la stratégie de parallélisme) est désormais ouvert, sous licence Apache, et conçu pour cibler deux plateformes dès le départ. Pour les petits laboratoires et les fabricants de matériel, c'est une fondation sur laquelle construire plutôt qu'un mur à reproduire. Quiconque suit le segment des modèles « flash » sait à quel point l'économie dépend de cela ; nous avons déjà défendu l'idée que les modèles de la gamme flash sont le vrai sujet de l'efficacité de l'inférence, et la pluralité matérielle l'amplifie.

La conception du parallélisme mérite aussi d'être mentionnée : parallélisme de données pour l'attention avec parallélisme de contexte équilibré, plus parallélisme d'experts pour les couches MoE avec un équilibreur de charge pour les experts. Cette combinaison est réglée précisément pour le déploiement décrit par DeepSeek : quelques GPU pour le prefill et des dizaines pour le decode, séparés. La séparation orientée decode devient l'architecture par défaut des grandes flottes de service, et la raison tient à une arithmétique simple : le prefill est limité par le calcul et le decode par la bande passante mémoire. Les faire cohabiter oblige donc à acheter un matériel optimal pour aucun des deux.

Mythe 6 : vous pouvez simplement le prendre et le faire tourner

Une dernière croyance à dégonfler, à l'intention de tous ceux qui ont mis une étoile au dépôt en prévoyant de l'intégrer à leur pile ce week-end. DeepInfer ne sert pour l'instant qu'une seule famille de modèles : DeepSeek-V4.1-Flash. Il exige Linux, GCC 13+, Python 3.12, CUDA 13 et du matériel SM100, c'est-à-dire de la génération Blackwell, que la plupart d'entre nous n'ont pas traînant dans un placard. Les benchmarks ont été mesurés sur 4 GPU pour le prefill et 64 pour le decode, au sein d'un même domaine de scale-up. Et les trois éléments nécessaires à un véritable service (le système de cache KV, la plomberie de séparation et l'équilibrage de charge entre réplicas) sont explicitement hors périmètre. C'est le moteur, pas la voiture.

Mais le rejeter comme inutilisable passe à côté du sujet, et c'est là que je me montrerai tranché. La publication vaut moins comme logiciel à déployer que comme une spécification de ce que signifie vraiment la qualité de production. L'inférence invariante au batch, en tant que drapeau de premier plan. L'échantillonnage déterministe avec un seq_seed fixe pour la reproductibilité. Les sorties structurées compilées dans l'échantillonneur plutôt que greffées avec des relances. La spéculation planifiée selon la confiance. L'ordonnancement asynchrone par défaut, et non comme expérimentation. Si vous construisez ou achetez une pile d'inférence, ce README sert de liste de contrôle. Si vous faites tourner vous-même de grands modèles (et nous avons un guide pratique pour faire tourner des LLM sur votre propre matériel pour le petit bout de la lorgnette), les mêmes priorités s'appliquent, simplement à une autre échelle : d'abord l'ordonnanceur, ensuite la disposition mémoire, et enfin les kernels.

La passe avant est la partie facile. L'inférence en production, c'est un ordonnanceur, un gestionnaire de mémoire et un système de vérification qui se trouvent appeler des kernels.

Le calendrier géopolitique, qui tombe quelques heures après un décret d'exclusion fédéral américain, avec des commentaires qui y voient tout, du cadeau au sabotage, va dominer les gros titres. Soit. Mais une fois le drame écarté, le verdict d'ingénierie tient seul : la plupart de ce que nous croyions sur les moteurs d'inférence avait une génération de retard. Le travail intéressant ne se trouve plus dans les kernels. Il est dans tout ce qui les entoure.