Auto-hébergement

Débogage d'une régression de performance d'un LLM auto-hébergé sur du matériel hérité

Un développeur a diagnostiqué un ralentissement sévère d'un modèle de 125 milliards de paramètres exécuté sur deux GPU Tesla P40, en traçant le problème jusqu'à une modification unique du code dans le moteur d'inférence.

Illustration comparant l'ancien et le nouveau matériel avec une accentuation sur le débogage
Illustration créée pour cet article

Traduit automatiquement depuis l’original en anglais.

Un développeur exécutant un grand modèle de langage sur du matériel grand public plus ancien a subi une chute soudaine de la vitesse d'inférence qui a duré une semaine. En éliminant systématiquement les problèmes de configuration et de matériel, il a tracé l'origine du problème vers une version spécifique du moteur open-source Strata. La résolution est venue d'un rapport de bug communautaire qui a identifié une ligne de code responsable de la régression.

Ce qui s'est passé

La configuration impliquait un serveur EPYC 7302 avec 128 Go de RAM et deux cartes graphiques Tesla P40 achetées pour 403 $. Ces cartes sont basées sur l'architecture Pascal de 2016, dépourvue de fonctionnalités modernes comme les Tensor Cores. Malgré l'âge de la puce, le système exécutait Qwen3.8-Flash-Next, un modèle à mélange d'experts (mixture-of-experts) comptant 125 milliards de paramètres. Le moteur d'inférence Strata gérait cela en conservant la plupart des poids des experts dans la mémoire vidéo, atteignant un taux de réussite du cache de 99,9 %.

Pendant une semaine en octobre, le système décodait le texte à seulement 17 ou 18 jetons par seconde. C'était nettement plus lent que les 23 jetons par seconde obtenus par un modèle dense plus petit sur le même matériel. C'était également plus lent que le moteur llama.cpp amont exécutant les mêmes poids. Le développeur a passé plusieurs jours à tester diverses hypothèses, notamment en changeant la stratégie de parallélisme, en supprimant la surcharge des conteneurs et en ajustant les tailles de lot. Aucune de ces modifications n'a amélioré la vitesse de décodage en flux unique.

La percée est survenue lorsque le développeur a trouvé un rapport de bug soumis par un autre utilisateur sur le tracker du projet Strata. Le rapport notait que la version 0.1.40.2 tournait à la moitié de la vitesse de la version 0.1.40.0 sur du matériel Pascal similaire. Le mainteneur avait identifié qu'un commit récent avait retiré le mot-clé __restrict__ des pointeurs d'activation et ajouté une instruction de préchargement que les architectures Pascal ne peuvent pas superposer au calcul. Le retour à la version 0.1.40.4, qui incluait la correction, a doublé la vitesse de décodage à plus de 35 jetons par seconde.

Détails clés

  • Le matériel se composait de deux GPU Tesla P40, qui ont une capacité de calcul 6.1 et aucun support NVLink.
  • La régression de performance affectait les versions Strata 0.1.40.2 et 0.1.40.3, faisant chuter les vitesses d'environ 36 à environ 17 jetons par seconde.
  • La cause racine était une modification du code qui introduisait une instruction de préchargement incompatible avec le pipeline d'exécution de l'architecture Pascal.
  • Le membre de la communauté paulhothersall a ouvert le ticket #1469, fournissant des données de benchmark qui ont isolé la plage de versions problématique.
  • Le mainteneur Niko1221 a poussé une correction environ 75 minutes après l'ouverture du ticket, restaurant le mot-clé __restrict__.
  • La mise à niveau vers la version 0.1.44.1 a fourni une augmentation de vitesse supplémentaire de 8 %, mais nécessitait de désactiver certains paramètres par défaut pour garantir une sortie cohérente.

Contexte

Les modèles à mélange d'experts divisent leurs paramètres en plusieurs groupes, activant uniquement un petit sous-ensemble pour chaque jeton généré. Cela permet aux modèles comportant des centaines de milliards de paramètres de fonctionner sur du matériel à mémoire limitée, car seuls les experts actifs doivent être chargés rapidement. Le moteur Strata optimise cela en épinglant une grande arène d'experts dans la RAM hôte et en mettant en cache ceux utilisés fréquemment dans la mémoire vidéo du GPU. Cela minimise les transferts de données via le bus PCIe, qui est souvent un goulot d'étranglement dans les configurations multi-GPU.

Le mot-clé __restrict__ en C++ indique au compilateur que deux pointeurs ne pointent pas vers des régions mémoire chevauchantes. Cela permet au compilateur de générer du code machine plus efficace en évitant les rechargements mémoire inutiles. Lorsque cette indication est retirée, ou lorsque des instructions de préchargement inappropriées sont ajoutées, le CPU ou le GPU peut marquer un temps d'arrêt en attendant les données, surtout sur les architectures anciennes qui manquent de capacités avancées d'exécution hors ordre. C'est pourquoi le bug n'affectait que les anciennes cartes Pascal et non le matériel plus récent.

Pourquoi c'est important

Pour les équipes gérant des infrastructures IA auto-hébergées, ce cas met en évidence l'importance de surveiller étroitement les versions logicielles. Des régressions de performance peuvent survenir sans aucun changement de configuration locale ou de matériel. Dans ce cas, le ralentissement était causé par une mise à jour amont qui avait involontairement cassé la compatibilité avec du matériel ancien mais encore largement utilisé. Sans un suivi rigoureux des versions, de tels problèmes peuvent entraîner une perte de temps d'ingénierie à déboguer des problèmes locaux inexistants.

Cela démontre également la valeur des projets open source pilotés par la communauté. Le bug a été trouvé et corrigé rapidement parce que les utilisateurs ont partagé des données de benchmark détaillées. Pour les organisations dépendant de matériel de niche ou hérité, interagir avec les trackers de tickets des projets peut fournir des résolutions plus rapides que le débogage interne. La transparence du projet Strata a permis au développeur de voir exactement quel commit avait causé le problème, permettant un retour arrière rapide et une vérification.

Enfin, cette histoire souligne que la vitesse brute de génération de jetons n'est pas la seule métrique qui compte. Même après avoir doublé la vitesse, les longues phases de "réflexion" dans les modèles complexes peuvent entraîner une latence significative avant l'apparition de tout texte. Les équipes doivent équilibrer l'optimisation du moteur avec le comportement du modèle pour répondre aux attentes en matière d'expérience utilisateur.

Ce que vous pouvez faire

  • Consultez les trackers de tickets de vos dépendances principales lors de baisses de performance inexplicables, même si votre configuration n'a pas changé.
  • Épinglez des versions spécifiques des moteurs d'inférence dans les environnements de production pour empêcher les régressions inattendues dues aux mises à jour automatiques.
  • Comparez vos résultats de benchmark avec les rapports publics d'autres utilisateurs disposant de matériel similaire pour identifier si un problème est répandu.
  • Testez les nouvelles versions du moteur en maintenant les paramètres par défaut constants pour isoler les changements de performance des changements de comportement du modèle.
  • Utilisez des overrides par requête pour les paramètres de réglage comme les portes de jetons brouillon afin de tester les améliorations sans redémarrer l'ensemble du service.
  • Maintenez un plan de retour arrière, comme la conservation des images Docker précédentes, pour revenir rapidement à un état connu bon si une mise à jour échoue.

Autres actualités

Toutes les actualités