La fusion CUDA de llama.cpp atteint jusqu’à 10,7 % de traitement des prompts en plus
llama.cpp a intégré une optimisation CUDA le 25 octobre, et les benchmarks du contributeur font état de gains de traitement des prompts atteignant 10,7 % sur DGX Spark. La modification de fusion CUDA de llama.cpp combine des opérations au sein du réseau feed-forward d’un modèle, le composant qui transforme les représentations de tokens entre les étapes d’attention. Son avantage concerne la lecture des prompts, plutôt que la production de chaque token de réponse ultérieur.
Les mesures du contributeur anujj montrent aussi des améliorations sur la RTX 5090 de NVIDIA, bien que les gains en pourcentage rapportés soient plus faibles. Il s’agit de comparaisons communiquées par l’auteur, et non de résultats reproduits de manière indépendante. La modification sous-jacente est vérifiable dans le code fusionné, tandis que sa valeur pratique dépend toujours du format du modèle, du matériel et de la charge de travail.
La comparaison centrale oppose l’exécution fusionnée, qui combine des opérations liées, à un chemin d’exécution existant qui planifie ces opérations séparément. Une proposition antérieure poursuivait la même opportunité d’une autre manière. L’approche fusionnée réorganise la façon dont les valeurs parviennent aux threads GPU, réduisant le travail intermédiaire sans modifier les poids stockés du modèle. Pour les développeurs exécutant de longs prompts localement, il s’agit donc d’une amélioration logicielle précise à mesurer par rapport à un déploiement existant.
Ce que change l’intégration de la fusion CUDA de llama.cpp
La modification fusionnée étend une optimisation existante aux charges de traitement de prompts plus importantes sur le matériel NVIDIA pris en charge.
CUDA, la plateforme de calcul GPU de NVIDIA, fournit l’environnement d’exécution de cette implémentation. MMQ, le chemin de multiplication matricielle quantifiée de llama.cpp, gère les calculs impliquant des poids de modèle compressés. La quantification représente ces poids avec une précision numérique réduite afin de diminuer les besoins en stockage et en mémoire.
La modification de fusion intégrée cible une séquence récurrente au sein des couches feed-forward denses prises en charge. Des projections gate et up distinctes transforment la même entrée. Une unité linéaire à porte, ou GLU, combine ensuite leurs résultats au moyen d’une activation et d’une multiplication élément par élément.
Auparavant, les charges plus importantes sur ce chemin traitaient les projections et leur combinaison comme des opérations distinctes. Selon anujj, la nouvelle implémentation exécute cette séquence au moyen d’un seul kernel de multiplication matricielle. Un kernel est une fonction lancée sur des threads GPU.
Cette consolidation est importante parce que le travail environnant consomme lui aussi des ressources. La préparation des entrées, le lancement des opérations, l’écriture de résultats intermédiaires et leur relecture peuvent tous ajouter une surcharge. Combiner les opérations offre l’occasion d’éliminer une partie de ce trafic tout en préservant la transformation mathématique prévue.
La modification n’introduit pas cette idée simultanément dans tous les chemins d’exécution. Le contributeur indique que la fusion pour petits lots existait déjà via d’autres implémentations matrice-vecteur. Les calculs matrice-vecteur servent généralement des charges plus petites que les calculs matrice-matrice mis en avant ici.
Cette distinction explique pourquoi l’annonce se concentre sur le traitement des prompts. Le préremplissage, la phase qui traite un prompt d’entrée avant la génération de la réponse, peut exposer des lots de travail plus importants. Le chemin d’exécution nouvellement pris en charge vise cette partie de l’inférence.
La chronologie compte également. La description d’ouverture de la pull request date du 4 octobre, tandis que le projet l’a fusionnée le 25 octobre. La fusion fait entrer l’implémentation dans la branche master du projet. Elle n’établit pas que chaque application distribuée inclut déjà cette modification.
Une application de bureau, un conteneur ou un projet en aval peut intégrer llama.cpp selon son propre calendrier. Les utilisateurs doivent donc identifier la version d’exécution réelle derrière une interface. Une mise à jour d’application et une fusion du code source en amont sont deux événements distincts.
Pour les fichiers de modèles existants, la question de compatibilité est relativement simple. Le contributeur indique que la disposition des poids stockés reste inchangée. Les utilisateurs ne doivent pas interpréter la référence du titre à des lignes entrelacées comme l’obligation de télécharger des fichiers de modèle spécialement réorganisés.
L’actualité immédiate est donc restreinte et concrète. Une séquence d’exécution prise en charge dispose désormais d’une optimisation supplémentaire disponible en amont. La question de savoir si une requête particulière l’utilise dépend des contrôles d’éligibilité de l’environnement d’exécution.
Les gains des benchmarks favorisent Spark, avec une hausse plus faible sur RTX 5090
Les résultats rapportés montrent une direction cohérente dans les configurations listées, mais leur ampleur varie selon le matériel et la quantification.
L’auteur a testé DGX Spark avec son processeur GB10 et a testé séparément une RTX 5090. Les deux tableaux de benchmarks comparent une référence libellée master à la pull request. Chaque ligne listée indique un débit de traitement des prompts supérieur pour l’implémentation modifiée.
Sur DGX Spark, la plus forte amélioration en pourcentage listée concerne Llama-3.1-8B utilisant IQ4_XS, un format de poids compressés. Le débit passe de 2 933,9 à 3 249,0 tokens par seconde. L’auteur rapporte un gain de 10,7 % pour cette configuration.
D’autres résultats Llama-3.1-8B sur Spark restent sous ce pic tout en conservant des améliorations rapportées substantielles. La configuration Q4_K_M passe de 2 749,3 à 3 000,6 tokens par seconde. La configuration Q5_K_M augmente de 2 699,4 à 2 960,3 tokens par seconde.
Ces libellés identifient différentes configurations de modèles quantifiés, plutôt que des mesures interchangeables d’un binaire identique. Leurs résultats montrent pourquoi les utilisateurs doivent faire correspondre le format de fichier exact lorsqu’ils évaluent une optimisation. Le seul nom d’une famille de modèles omet une variable pertinente.
Le tableau Spark liste également des modèles libellés Qwen3.8-27B et Qwen3.6-27B. Ces noms sont reproduits ici comme libellés de benchmark issus du rapport du contributeur. Le tableau de performances lui-même n’établit pas indépendamment leurs détails d’entraînement, leurs capacités ou leur disponibilité plus large.
Pour l’entrée libellée Qwen3.8-27B, les améliorations sur Spark vont de 4,6 % à 7,4 %. La ligne Q4_K_M passe de 840,1 à 899,9 tokens par seconde. La ligne IQ4_XS augmente de 882,4 à 948,0 tokens par seconde.
Les résultats de la RTX 5090 présentent une échelle d’amélioration différente. Dans ses configurations listées, l’auteur rapporte des gains allant de 1,9 % à 4,7 %. Cette plage de pourcentage plus faible rend l’interprétation spécifique au matériel essentielle.
Pour Llama-3.1-8B en IQ4_XS, le résultat sur RTX 5090 passe de 12 997,6 à 13 603,3 tokens par seconde. L’amélioration rapportée est de 4,7 %. Pour l’entrée Qwen3.8-27B Q6_K, le débit passe de 3 220,5 à 3 281,0 tokens par seconde, soit 1,9 %.
Les benchmarks de fusion de llama.cpp étayent donc une conclusion circonscrite. Dans les tests de longs prompts sélectionnés par l’auteur, l’optimisation améliore le débit d’entrée mesuré dans la matrice rapportée. Ils n’établissent pas une hausse identique pour chaque GPU pris en charge.
La comparaison en pourcentage ne révèle pas non plus pourquoi Spark en bénéficie davantage. Des comportements mémoire distincts, des caractéristiques d’exécution et des goulots d’étranglement existants peuvent influencer les résultats d’optimisation. Établir leur contribution relative nécessiterait un profilage supplémentaire, que ces seules mesures principales ne fournissent pas.
Pour les acheteurs, l’implication utile concerne la rigueur d’évaluation. Ces résultats ne justifient pas de choisir du matériel sur la seule base des gains en pourcentage. Le débit absolu, l’adéquation du modèle, la capacité mémoire et la latence réelle de l’application restent des considérations distinctes.
Les lignes entrelacées réduisent le travail entre les calculs
Le mécanisme central de l’implémentation consiste à disposer des résultats associés ensemble avant d’appliquer l’opération de porte.
Anujj décrit le chargement alterné de lignes gate et up par blocs de huit. Cette disposition place les valeurs correspondantes dans les résultats de travail d’un même thread. Le thread peut alors combiner ces valeurs lors de l’écriture de la sortie finale.
Les registres, les petites zones de stockage du GPU attribuées aux threads en cours d’exécution, conservent ces résultats intermédiaires. Appliquer la combinaison à cet endroit évite un aller-retour distinct par des buffers de sortie intermédiaires pour cette partie du calcul. Le gain visé provient de l’organisation de l’exécution.
L’implémentation conserve une disposition d’accumulateur au lieu d’introduire une seconde tuile d’accumulateur pour la projection gate. Un accumulateur conserve les sommes partielles pendant une multiplication. Une tuile est un bloc d’éléments de matrice traités ensemble.
Ce choix de conception distingue cette implémentation de la proposition de fusion antérieure. Selon la description du contributeur, la nouvelle approche suit une suggestion d’am17an. Les deux propositions ciblent des opérations liées, mais organisent le calcul différemment.
La distinction est pratique plutôt que cosmétique. Conserver davantage d’état intermédiaire peut accroître la pression sur les ressources d’exécution limitées d’un GPU. Combiner les opérations n’aide que lorsque les économies dépassent tout coût supplémentaire de planification ou de stockage.
C’est pourquoi « moins de kernels » ne signifie pas automatiquement « inférence plus rapide ». Une opération combinée plus importante peut utiliser le matériel moins efficacement dans certaines conditions. Une optimisation doit préserver suffisamment de travail parallèle tout en évitant de nouvelles contraintes qui annuleraient ses avantages.
Le code fusionné reflète cette préoccupation par des contrôles d’éligibilité. Il évalue si la charge de travail convient à ce chemin de fusion. La seule prise en charge d’opérations mathématiques ne garantit pas que la fusion doit s’exécuter.
L’implémentation est aussi en concurrence avec stream-k, une méthode de planification qui divise le travail de multiplication matricielle selon sa dimension de réduction. Le chemin fusionné ne peut pas utiliser cette décomposition. Il doit donc disposer d’un autre moyen de déterminer si sa disposition en tuiles offre une efficacité suffisante.
Johannes Gaessler a soulevé ce point durant la revue, demandant une comparaison préalable entre l’efficacité attendue du découpage en tuiles et stream-k. Le code fusionné inclut une décision d’efficacité avant de sélectionner le chemin fusionné. Cela fait de l’exécution conditionnelle une partie de la conception.
La revue de planification de la fusion offre la perspective sceptique la plus utile sur l’optimisation. Elle se concentre sur les circonstances dans lesquelles une simplification séduisante pourrait entraîner une perte de performances. Il s’agit d’une limite technique, plutôt que d’un désaccord sur l’existence du benchmark.
La concurrence principale demeure donc au sein de l’environnement d’exécution. L’exécution fusionnée doit surpasser l’approche de planification existante pour la forme d’opération éligible. Ces résultats n’apportent aucune preuve d’une victoire plus large sur un autre framework d’inférence.
Ce mécanisme clarifie également ce que les utilisateurs doivent attendre de la fusion CUDA de llama.cpp. Elle réorganise le travail pour un calcul existant. Elle n’augmente pas la capacité de raisonnement d’un modèle, n’étend pas ses connaissances acquises et ne supprime pas les limites de mémoire sans rapport.
Le même modèle accomplit toujours la même tâche générale. L’opportunité d’ingénierie consiste à consacrer moins de temps à atteindre la sortie d’une séquence particulière.
Un traitement des prompts plus rapide ne modifie qu’une partie du temps d’attente
Le benchmark mesure le traitement des entrées ; sa valeur pratique dépend donc de la part du traitement des entrées dans une requête réelle.
La commande rapportée traite un prompt contenant 16 384 tokens et demande zéro token généré. Les tokens sont les unités de texte qu’un modèle consomme et produit. Cette configuration isole le débit des prompts au lieu de mesurer une réponse conversationnelle complète.
La commande utilise des tailles de batch et de microbatch de 2 048. Un microbatch est une unité d’exécution plus petite découpée dans un batch plus important. Elle active également Flash Attention, une implémentation de l’attention conçue pour réduire les transferts mémoire lors des calculs d’attention.
L’auteur spécifie un stockage en demi-précision pour le cache clé-valeur, qui conserve les informations d’attention issues des tokens déjà traités. Cinq répétitions figurent dans la commande. Ces paramètres définissent les conditions testées et doivent accompagner tout résultat cité.
La méthodologie de benchmark du projet distingue le traitement des prompts de la génération de tokens. Cette séparation est essentielle pour interpréter ces chiffres. Les tokens d’entrée par seconde et les tokens de sortie par seconde répondent à des questions de performance différentes.
Prenons le cas d’un développeur demandant à un modèle local d’examiner un volumineux fichier source. Le traitement de cette entrée peut contribuer de façon significative au délai avant le début de la réponse. Un chemin de préremplissage éligible plus rapide agit sur cette partie de l’interaction.
Un bref échange conversationnel représente une autre charge de travail. Son prompt peut nécessiter peu de traitement par rapport au temps consacré à générer une longue réponse. La même amélioration du débit d’entrée affecterait alors une part plus réduite de l’attente totale.
Les prompts mis en cache introduisent une autre distinction. La mise en cache des prompts réutilise les calculs liés à des entrées que le système a déjà traitées. Lorsqu’une application réutilise effectivement ce travail, il reste moins de nouvelles entrées à accélérer grâce à cette optimisation.
L’analyse de documents comprend aussi des tâches au-delà de l’inférence. Une application peut récupérer des fichiers, extraire du texte, classer des passages ou préparer une instruction avant d’envoyer quoi que ce soit au modèle. Un traitement plus rapide des entrées du modèle ne permet pas d’affirmer que ces étapes périphériques se terminent plus tôt.
Pour les équipes qui maintiennent une base de connaissances consultable, cette séparation facilite de meilleures décisions de performance. La qualité de récupération et la sélection du contexte déterminent ce qui parvient au modèle. Le débit d’exécution détermine la vitesse à laquelle les calculs éligibles le traitent.
Cette distinction impose également des exigences aux responsables d’applications. Ils doivent savoir quel runtime leur produit embarque et si ses charges de travail habituelles empruntent le chemin optimisé. Une note de version générique annonçant une IA locale plus rapide ne peut répondre à ces questions.
La réponse utile consiste à mesurer à la frontière de l’application. Le délai avant le premier token mesure l’attente avant le début de la sortie. Le temps de réponse total mesure l’intégralité de l’interaction, tandis que le débit de génération mesure le rythme après le début de la sortie.
Chaque métrique peut évoluer différemment après une mise à jour du runtime. Une amélioration du préremplissage peut réduire l’attente initiale sans modifier le rythme du texte qui suit. Rapporter ces résultats séparément donne aux utilisateurs une vision plus précise de la mise à niveau.
Pour le traitement des prompts de llama.cpp, le cas d’usage le plus solide reste une entrée nouvelle et substantielle dans une configuration éligible. Le benchmark de l’auteur met délibérément l’accent sur cette charge de travail. Étendre son résultat à toutes les sessions de chat irait au-delà des éléments disponibles.
Les formats pris en charge et les contrôles de correction fixent les limites
Le code fusionné comporte des restrictions importantes, et le rapport de correction de l’auteur doit être interprété dans le cadre de la couverture de test qu’il indique.
La pull request décrit la prise en charge des types de poids Q4_K, Q5_K, Q6_K, Q8_0 et IQ4_XS sur les architectures NVIDIA Turing ou plus récentes. Il s’agit des familles de poids prises en charge. Les fichiers de modèles peuvent combiner plusieurs types ; l’étiquette globale d’un fichier ne décrit donc pas chacun de ses tenseurs internes.
L’implémentation vérifie également que les poids de gate et up concernés répondent aux conditions nécessaires à la fusion. La forme, le type et la disposition comptent, car l’opération combinée attend des calculs correspondants. Un agencement non pris en charge doit conserver un autre chemin d’exécution.
Le code fusionné exclut les cas nécessitant son mécanisme de repli aux limites de ligne. Ce mécanisme traite les formes qui ne remplissent pas proprement les tuiles de traitement normales. Cette restriction importe, car la description initiale évoque des tests couvrant des cas limites durant le développement.
Les lecteurs doivent donc distinguer le résumé initial des tests des règles de sélection du code final. Tester une implémentation de développement plus large n’établit pas que chaque forme testée reste éligible dans la version fusionnée. Les protections finales déterminent l’exécution réelle.
Des restrictions s’appliquent aussi aux paramètres des opérations. Le code vérifie certains réglages d’activation avant de sélectionner la fusion. Ces vérifications empêchent le runtime de considérer des opérations superficiellement semblables comme interchangeables lorsque leur comportement requis diffère.
Le périmètre se concentre aussi sur les opérations feed-forward denses. Les modèles mixture-of-experts, qui orientent les tokens vers des sous-réseaux sélectionnés, introduisent des exigences d’exécution différentes. La revue identifie explicitement un travail de prise en charge supplémentaire, plutôt que d’établir une couverture universelle pour ces modèles.
Les autres backends matériels ne bénéficient pas de cette optimisation simplement parce qu’ils prennent en charge llama.cpp. Le contributeur distingue explicitement cette modification des autres backends. Les résultats sur matériel NVIDIA ne permettent donc pas d’établir des gains équivalents sur un GPU Apple ou une configuration AMD.
Les éléments de correction sont encourageants, mais restent rapportés par l’auteur. Anujj indique que l’ensemble complet de tests des opérations backend réussit sur GB10. Le rapport indique aussi que les suites de multiplication matricielle réussissent sur une RTX 5090.
Le contributeur rapporte en outre des résultats de perplexité identiques sur les tests spécifiés, avec la fusion activée et désactivée. La perplexité mesure la capacité d’un modèle à prédire un texte d’évaluation. Elle fournit un contrôle numérique utile, même si elle ne représente pas tous les comportements possibles d’une application.
Ce résultat étaye une affirmation plus restreinte qu’une identité universelle des sorties. Des scores identiques sur une évaluation donnée ne prouvent pas l’identité des réponses générées pour tous les prompts, réglages ou plateformes. Une validation plus large exige des preuves plus étendues.
Les benchmarks de fusion de llama.cpp ne disposent pas non plus d’une reproduction indépendante parmi les sources examinées pour ce rapport. La revue et la fusion en amont établissent que le projet a accepté le code. Elles ne transforment pas les mesures du contributeur en audit externe des performances.
Aucune de ces limites n’efface le gain rapporté. Elles définissent le cadre dans lequel l’affirmation devient utile. Un lecteur disposant d’un matériel et de types de modèles correspondants a une raison concrète de tester, tandis que les autres configurations nécessitent des éléments distincts.
L’interprétation la plus solide combine ces deux aspects. La modification présente un mécanisme clair et des mesures publiées, accompagnés de conditions explicites qui déterminent son exécution.
Trois signaux montreront dans quelle mesure l’amélioration se propage
Les prochains éléments devront relier le code en amont, des mesures reproductibles et la latence ressentie par les utilisateurs.
Le premier signal est l’adoption en aval. Au cours des prochains mois, les versions d’applications et les builds empaquetés devraient établir quels produits incluent l’implémentation fusionnée. C’est important, car les utilisateurs interagissent souvent avec llama.cpp par l’intermédiaire d’une autre application.
Les instructions de compilation CUDA du projet décrivent la voie de compilation en amont. Les distributions empaquetées peuvent suivre des calendriers de publication différents. Un identifiant de version lié au code source inclus est plus informatif qu’une affirmation générale d’utiliser le backend le plus récent.
Si des applications largement utilisées intègrent la modification, le cas d’adoption immédiate se renforce. Si elles restent sur des révisions plus anciennes, la portée pratique de la fusion demeure limitée. Cette situation concernerait le calendrier de distribution plutôt qu’elle n’invaliderait l’optimisation sous-jacente.
Le deuxième signal est une reproduction indépendante avec des réglages explicites. Les rapports utiles doivent identifier le matériel, les fichiers de modèles, les révisions du runtime et les arguments de benchmark. Ils doivent aussi séparer le traitement des prompts nouveaux des entrées en cache et de la sortie générée.
Des reproductions sur différentes longueurs de prompts préciseront à quel point le gain dépend de la taille de la charge de travail. Des rapports couvrant plusieurs générations de GPU prises en charge testeront si la tendance persiste au-delà des machines indiquées. Des résultats montrant des régressions aideraient à identifier les limites de la logique de sélection.
La question pertinente n’est pas de savoir si chaque exécution atteint le maximum annoncé. Il s’agit de déterminer si des charges de travail comparables affichent une amélioration répétable avec une variation acceptable. Un gain plus modeste mais constant peut tout de même être utile sur le plan opérationnel.
Le troisième signal est la latence de l’application. Les équipes doivent mesurer si un runtime mis à jour réduit l’attente avant le début d’une réponse sur des requêtes représentatives. Elles doivent conserver le débit de génération et le temps de réponse total comme mesures distinctes.
Ces éléments relieraient la modification du kernel à l’expérience utilisateur. Un délai initial plus faible sur des prompts longs non mis en cache étayerait la promesse pratique de l’implémentation. Un temps de réponse total inchangé indiquerait que d’autres étapes dominent le flux de travail mesuré.
Une prise en charge étendue de formats ou d’architectures constituerait un développement supplémentaire, et non un élément établi par cette fusion. Les affirmations futures devront identifier leur propre implémentation et leurs propres mesures. Les résultats actuels fournissent une référence de comparaison, pas une confirmation anticipée de ces extensions.
Pour l’instant, la fusion CUDA de llama.cpp offre une raison précise de réexaminer les performances de l’inférence locale. Elle cible un travail répété dans des calculs feed-forward éligibles et rapporte des gains mesurables dans des conditions divulguées de traitement des prompts. Cela suffit à justifier une évaluation sans promettre une hausse de vitesse généralisée.
Avant de mettre à jour un déploiement en production, conservez son identifiant de runtime actuel et un ensemble représentatif de requêtes. Comparez les mêmes fichiers de modèles, longueurs de prompts, conditions de cache et réglages de sortie après la mise à jour. Enregistrez séparément le temps d’attente initial et la génération de réponse, puis conservez les arguments de benchmark avec chaque résultat. Pour les flux de travail sur de longs documents, incluez des entrées nouvelles qui ressemblent aux usages quotidiens au lieu de vous appuyer uniquement sur un débit synthétique. Pour les charges conversationnelles, vérifiez si un délai de traitement des entrées plus court améliore suffisamment l’expérience pour avoir de l’importance. Utilisez ensuite vos propres mesures pour décider où cette amélioration en amont s’inscrit dans votre calendrier de publication. Le runtime mis à jour raccourcit-il réellement la partie de la requête que vos utilisateurs attendent ?



