Redémarrage d'un modèle de mille milliards de paramètres : de 8,8 minutes à 32 secondes

Le 21 août, l'équipe infrastructure Ling d'Ant, avec Alibaba et l'équipe SGLang, ont présenté sur le blog de LMSYS un composant baptisé Weight Cache Daemon : un processus résident en mémoire GPU qui conserve les poids déjà quantifiés et découpés, pour qu'un nouveau moteur d'inférence les récupère par copie nulle via CUDA IPC. Selon les chiffres de l'équipe, le chargement des poids FP8 de Ling-2.6-1T passe d'environ 495 secondes à 0,63 seconde, et le démarrage complet de 8,8 minutes à 0,528 minute.

Huit des huit minutes et demie passent à lire le disque

L'équipe a décomposé un démarrage complet pour le mesurer étape par étape. Ling-2.6-1T FP8 tourne sur 8 GPU H20-3e, avec les poids stockés sur un SSD NVMe de 3,5 To ; du démarrage jusqu'à la capacité de traiter des requêtes, il faut environ 527 secondes. Le chargement des poids en occupe 495, soit 93,9 % ; l'initialisation du tokenizer prend 13 secondes, l'initialisation distribuée de torch 5 secondes, la capture du CUDA graph 7,7 secondes, et le reste moins de 6 secondes au total.

Chaque carte doit lire sur disque environ 120 Go de safetensors, les désérialiser, les découper selon le parallélisme de tenseurs, puis appliquer la quantification FP8 et réorganiser les poids, soit 161 fragments au total. Ce pipeline repart de zéro à chaque redémarrage, alors que son résultat est déterministe : pour un même modèle et une même configuration, les tenseurs qui finissent en mémoire sont toujours identiques — et souvent encore présents juste après l'arrêt du processus précédent.

En production, ces quelques minutes se traduisent par une latence de queue P99 qui bondit pendant le redémarrage, des requêtes en cours qui échouent en bloc ou restent bloquées indéfiniment en file d'attente, et des mises à jour progressives comme des reprises après incident entièrement soumises à ce cycle.

Les poids restent en mémoire GPU

Chaque GPU fait tourner un processus daemon, un par TP rank. Il charge une fois les poids en suivant le pipeline complet depuis le disque, puis exporte tous les paramètres et buffers de model.state_dict() sous forme de handles CUDA IPC, transmis via un socket Unix aux processus moteurs qui se connectent. Le moteur construit d'abord la structure du modèle sur le meta device, sans allouer de mémoire, puis fait pointer le pointeur data de chaque paramètre vers le tenseur mappé. Les deux processus partagent la même mémoire physique, sans aucune copie. Les paramètres de post-traitement générés pendant la quantification FP8, comme weight_scale, sont également mis en cache, ce qui évite de requantifier.

Deux verrous de sécurité ont été mis en place. Le premier est une empreinte de configuration : le chemin du modèle, le découpage TP/PP/DP, la méthode de quantification avec son hash de configuration et le dtype doivent correspondre, avec en plus la capacité de calcul du GPU et la version de torch enregistrées comme marqueurs d'environnement. La raison : des architectures ou des versions de torch différentes empruntent des chemins de post-traitement différents — les poids peuvent se mapper proprement mais donner des résultats aberrants. En intégrant l'environnement dans l'empreinte, cette erreur silencieuse devient une incompatibilité explicite, qui fait retomber le système sur un chargement depuis le disque.

Le second verrou est une liste blanche des méthodes de quantification. Pour l'instant, seules l'absence de quantification et le FP8 block-wise sont validés ; per-tensor FP8, Marlin et AWQ/GPTQ déclenchent directement une erreur. La raison : IPC n'exporte que les données brutes du tenseur, alors que ces méthodes laissent une partie de leur effet dans des métadonnées côté Python, ou réordonnent et transposent les poids — le mapping produirait alors des valeurs fausses. L'équipe a préféré un échec explicite plutôt qu'un résultat erroné livré en silence.

Si le daemon plante, le moteur déjà en cours d'exécution n'est pas affecté : le compteur de références CUDA reste actif, et la mémoire n'est libérée que lorsque les deux côtés se sont arrêtés. Une fois le daemon relancé, il relit le disque et réexporte les handles, et seulement alors un nouveau moteur peut s'y reconnecter.

Au passage, la facture du hot standby est réglée

Le composant propose trois modes : daemon (le moteur lance lui-même le daemon, le premier démarrage reste lent), client (se connecte à un daemon déjà actif, redémarrage en moins d'une seconde) et off (le mode par défaut, qui passe par le disque ; pour Ling-2.6-1T, cela prend de 405 à 411 secondes).

Plus concrets encore que l'accélération du démarrage, plusieurs schémas de déploiement. Plusieurs instances de moteur sur la même carte mappent le même jeu de poids : le disque n'est lu qu'une fois, la quantification n'est faite qu'une fois. Un service en ligne prioritaire et un traitement par lots hors ligne de priorité moindre se partagent une carte ; quand ce dernier est préempté, il redémarre en moins d'une seconde. Lors d'un basculement, le moteur de secours se raccroche par copie nulle au même jeu de poids et reste « chaud » ; si l'instance principale tombe, le secours prend le relais en moins d'une seconde.

Ce dernier point est un poste de coût manifeste. Estimation grossière : une instance Ling-2.6-1T occupe 8 GPU H20-3e ; un hot standby classique implique de laisser tourner à vide 8 cartes supplémentaires à côté. Avec un backup à poids partagé, c'est exactement ce groupe de cartes mobilisées en permanence qui disparaît. Même logique pour les mises à jour progressives : à raison d'environ 8,8 minutes par cycle, chaque instance brûle environ 1,2 heure-GPU à vide par tour de redémarrage, et sur un cluster de plusieurs centaines d'instances, un seul tour de mise à jour représente des centaines d'heures-GPU.

Ce n'est pas encore fini

Weight Cache Daemon constitue la première étape du Fast Engine Recovery Framework. La feuille de route vise un démarrage à froid sous les 10 secondes et un basculement de hot standby sous la seconde. Sont ensuite prévus la sérialisation du CUDA graph, un cache de kernels et l'optimisation de l'initialisation distribuée. D'après la décomposition ci-dessus, ces éléments représentent au total environ 26 secondes — précisément ce qui reste comme principal poste après les poids.

Des chiffres pour la catégorie Qwen3-235B FP8 ont également été publiés : environ 235 Go de poids, 306 à 327 secondes de chargement depuis le disque, moins d'une seconde pour le mapping IPC, soit un facteur d'environ 500. Pour Ling-2.6-1T, ce facteur atteint environ 780.

Le billet de blog mentionne au passage le tout récent Kimi K3, à 2,8 trillions de paramètres. Plus le nombre de paramètres augmente, plus le temps de chargement depuis le disque croît de façon quasi linéaire, alors que le mapping par IPC reste pratiquement constant — un écart qui ne fera que se creuser. Pour les équipes qui font déjà tourner des modèles à l'échelle du trillion de paramètres, le coût d'un redémarrage est en train de passer d'« attendre quelques minutes » à « ne quasiment plus attendre », ce qui pèse non seulement sur les chiffres de disponibilité, mais aussi sur l'audace à modifier fréquemment les configurations et à loger deux services sur la même carte.

Sources: blog technique de LMSYS Org, CocoLoop, documentation du projet SGLang ; la décomposition du temps de démarrage ainsi que les chiffres comparatifs de chargement des poids et de mapping IPC proviennent du tableau de benchmark mono-nœud publié par l'équipe.