Partage du processus de reproduction et de correction de deux bugs de race condition du KV Cache rencontrés lors de l’exploitation à l’échelle de centaines de millions de requêtes d’un Coding Agent basé sur GLM-5, ainsi que d’optimisations ayant amélioré le débit.
Contexte
Les lois de mise à l’échelle (Scaling Laws) n’ont pas seulement poussé les innovations en matière de paramètres de modèles et de volume de données, elles poussent aussi l’ingénierie d’infrastructure à ses limites. Z.ai appelle les effets secondaires de ce phénomène le Scaling Pain.
Alors que la série GLM-5 traitait chaque jour des centaines de millions de charges de travail complexes de Coding Agent, certains utilisateurs ont signalé des anomalies telles que sorties corrompues (garbled output), génération répétitive et production de caractères rares. Ces problèmes ne se reproduisaient jamais dans les environnements d’inférence standard, et n’apparaissaient que dans des contextes de forte concurrence et de longs contextes.
Résumé des résultats clés
| Élément | Valeur |
|---|---|
| Taux de sorties anormales après l’application du Bug Fix #1 | 0.1% → moins de 0.03% |
| Amélioration du débit avec LayerSplit (40K~120K tokens) | +10% ~ +132% |
| Correctif HiCache contribué via la PR SGLang #22811 | ✅ |
Détection des anomalies : exploitation des métriques de Speculative Decoding
Détecter automatiquement ces anomalies constituait en soi un défi. Les heuristiques de type expressions régulières généraient trop de faux positifs et de faux négatifs, et les classifieurs basés sur des modèles coûtaient trop cher pour des expériences à grande échelle.
La percée est venue des métriques de Speculative Decoding.
- Sorties corrompues / caractères rares :
spec_accept_lengthextrêmement faible → signal d’un désalignement de l’état du KV Cache entre le modèle draft et le modèle cible - Génération répétitive :
spec_accept_rateextrêmement élevé → signal qu’un KV Cache endommagé fait converger le motif d’attention vers une boucle répétitive
Sur cette base, une stratégie de monitoring en ligne a été mise en place. Si, après plus de 128 tokens générés, spec_accept_length < 1.4 ou spec_accept_rate > 0.96, la génération est immédiatement interrompue et un retry est confié au load balancer. En d’autres termes, le Speculative Decoding est passé d’outil d’optimisation des performances à outil de monitoring en temps réel de la qualité des sorties.
Bug Fix #1 : race condition du KV Cache dans l’architecture à séparation PD
Cause
Dans l’architecture à séparation PD (Prefill-Decode), un mécanisme d’interruption de requête basé sur timeout est utilisé pour contrôler la tail latency. Si le Prefill n’est pas terminé dans le délai imparti, le côté Decode abort la requête concernée et récupère le KV Cache.
Le problème est que le signal d’abort n’est pas correctement transmis au côté Prefill. Même après que Decode a récupéré le KV Cache et l’a réaffecté à une nouvelle requête (Req2), le RDMA write et les opérations Prefill de la requête précédente (Req1) continuaient, ce qui entraînait l’écrasement du KV Cache de Req2.
Correction
Après l’émission de l’abort par Decode, une notification est désormais envoyée au côté Prefill, et Prefill ne renvoie un signal « sûr à récupérer » que lorsqu’une des deux conditions suivantes est satisfaite :
- le RDMA write n’a pas encore commencé
- tous les writes déjà émis sont terminés
Decode ne réutilise le KV Cache qu’après avoir reçu cette confirmation. Après déploiement, le taux de sorties anormales est passé de 0.1% à moins de 0.03%.
Bug Fix #2 : absence de garantie d’ordre Load-Use dans HiCache
Cause
Les charges de travail de Coding Agent ont une longueur d’entrée moyenne supérieure à 70K tokens et un taux élevé de réutilisation des préfixes. Pour cela, HiCache (KV Cache hiérarchique) est utilisé, avec une architecture qui swap-in le KV Cache de manière asynchrone depuis la mémoire CPU tout en faisant se chevaucher le Load Stream et le Forward Stream.
Le problème venait du fait que le kernel Indexer ne spécifiait pas explicitement de contrainte de synchronisation avec la fin du chargement de l’Indexer cache. Si le Forward Stream démarrait avant le Load Stream, un schéma de type read-before-ready se produisait, lisant un KV Cache pas encore chargé, ce qui menait à des sorties anormales.
Correction
Un point de synchronisation explicite avec le Load Stream a été inséré avant l’exécution du kernel Indexer, afin que le Forward Stream ne poursuive le calcul qu’une fois les données prêtes. Ce correctif a été contribué à la communauté SGLang via la PR #22811.
Optimisation : LayerSplit (stockage distribué du KV Cache par couche)
Le goulet d’étranglement commun aux deux bugs était la charge du stage Prefill lui-même. Pour l’améliorer à la racine, LayerSplit a été conçu et implémenté.
Jusqu’ici, dans un environnement de Context Parallelism (CP), chaque GPU stockait de façon redondante le KV Cache de toutes les couches. Avec LayerSplit, le stockage est réparti de sorte que chaque GPU ne prenne en charge qu’une partie des couches, ce qui réduit fortement l’usage mémoire par GPU.
À l’exécution, le rang CP qui possède le KV Cache de la couche concernée diffuse le cache avant l’opération d’attention. Le broadcast est exécuté en chevauchement avec l’opération indexer pour masquer le surcoût de communication, et comme les données supplémentaires échangées se limitent à l’indexer cache (environ 1/8 de la taille du KV Cache), le surcoût global reste négligeable.
Avec un taux de cache hit de 90 %, le débit s’est amélioré de 10 % à 132 % sur des requêtes de 40K à 120K tokens, et le gain augmente avec la longueur du contexte.
Conclusion
« Le débit, la latence et la disponibilité ne suffisent pas. Le système doit aussi garantir la justesse de l’état du modèle derrière chaque requête de génération. Les lois de mise à l’échelle poussent les capacités à l’extrême, mais la seule manière de rendre ces capacités fiables à grande échelle, c’est une ingénierie système rigoureuse. »
Source : Z.ai Research Blog (2026-04-30)
Aucun commentaire pour le moment.