- Sur un serveur équipé de deux Xeon E5-2690 v2 de 2013 et de DDR3, Gemma 4 26B-A4B Q8_0 a pu être exécuté uniquement sur CPU, avec environ 5,2 tokens/s en décodage et 16 tokens/s en évaluation de prompt
- Le chemin rapide de
ik_llama.cppsuppose AVX2·FMA3, mais les CPU Ivy Bridge ne prennent en charge qu’AVX1 ; il a donc fallu modifier la compilation pour un build sans AVX2 et ajouter des fallbacks d’exécution - Le constructeur de graphe générait systématiquement
MOE_FUSED_UP_GATEetFUSED_UP_GATE, alors que le dispatcher non AVX2 n’avait aucun chemin pour les traiter, ce qui laissait environ 240 tenseurs non calculés à chaque passe avant et produisait une sortie multilingue incohérente - Claude a identifié l’erreur via l’instrumentation des logits et l’analyse du code, puis a remplacé les opérations fusionnées par deux
ggml_mul_mat_idetggml_fused_mul_unary, tandis que l’utilisateur exécutait les expériences et déterminait le référentiel d’un résultat correct - Même un ancien serveur d’entreprise peut servir de solution locale de secours en cas de panne d’API payante ou pour des traitements batch lents, mais il ne faut pas utiliser
--run-time-repack, qui produit une disposition spécifique à AVX2
Environnement d’exécution sur un serveur de stockage vieux de 13 ans
- L’équipement HP StoreVirtual recyclé utilise deux Xeon E5-2690 v2 de 2013 et de la mémoire DDR3, sans GPU
- Il appartient à la génération Ivy Bridge, donc ne prend en charge qu’AVX1 et pas AVX2 ni FMA3
- Il a été conçu à l’origine pour le stockage sur disque et a coûté moins de 300 dollars
- Le modèle exécuté est Gemma 4 26B-A4B de Google, un modèle open weight Mixture of Experts (MoE) de 26 milliards de paramètres
- Sur le modèle quantifié Q8_0, les mesures donnent environ 5,2 tokens/s en décodage et 16 tokens/s en évaluation de prompt
Le point de départ : un cas sur Xeon de 2016
- L’article partagé sur Hacker News, A 10 year old Xeon is all you need, montre l’exécution de Gemma 4 sans GPU sur un Xeon unique de 2016 avec 128 Go de DDR3
- Cette configuration utilise ik_llama.cpp et une vingtaine de drapeaux d’exécution très précis
- décodage spéculatif
- routage MoE adapté au CPU
- flash attention pour CPU
- repacking des poids à l’exécution
- La même approche a été appliquée au serveur Ivy Bridge, mais l’exécution s’arrêtait dès le démarrage
- Contrairement au CPU Broadwell de 2016, le E5-2690 v2 ne dispose ni d’AVX2 ni de FMA3
- Cet ensemble d’instructions est disponible à partir de Haswell en 2014, soit la génération v3 d’Intel, et les kernels rapides supposaient sa présence
Diagnostic et patch avec Claude
- Après avoir reçu les informations sur l’échec au démarrage, Claude a identifié comme cause la différence de jeu d’instructions CPU
- Reprenant une tentative inaboutie effectuée auparavant avec un modèle gratuit, il a modifié les chemins C++ critiques pour que les fallbacks fonctionnent correctement sur les CPU antérieurs à AVX2
- Le travail ne s’est pas résumé à une simple demande du type
fix it- lecture de code C++ écrit par d’autres développeurs avec un fort accent sur la performance
- analyse de la raison pour laquelle certains kernels n’étaient pas valides sur une microarchitecture donnée
- contournement des chemins non pris en charge sans supprimer les optimisations déjà présentes dans le fork
- Au lieu d’écrire lui-même les fallbacks des kernels C++, l’utilisateur a exécuté les expériences, lu les sorties, formulé les questions suivantes et déterminé le référentiel d’un résultat correct
- Le diagnostic et le patch ont été réalisés par une instance de Claude exécutée sur ce même serveur
Chemins d’opérations cassés dans un build sans AVX2
- Le
ik_llama.cpputilisé pour l’inférence MoE de Gemma 4 est un fork dellama.cppqui suppose par défaut AVX2 comme condition minimale - Lors de la compilation, désactiver
GGML_USE_IQK_MULMATretire la plupart des chemins rapides au profit de fallbacks scalaires/SSE plus génériques- Ce fallback fonctionne pour la multiplication de matrices Q8_0 classique
- En revanche, il ne s’appliquait pas à deux opérations du graphe
- Le réseau feed-forward MoE de Gemma 4 génère les opérations suivantes
MOE_FUSED_UP_GATE: opération fusionnée combinant, par expert, les multiplications de matrices gate et up avec SwiGLUFUSED_UP_GATE: version dense utilisée dans les couches non MoE
- Dans le dispatcher de calcul, ces deux opérations étaient protégées par la condition
GGML_USE_IQK_MULMAT, mais le constructeur de graphe les générait sans condition- Le dispatcher du build sans AVX2 ne contenait aucun
casepour traiter ces enums - Les opérations tombaient dans la branche par défaut, et les tenseurs de destination de tous les réseaux feed-forward experts restaient silencieusement non calculés
- Le dispatcher du build sans AVX2 ne contenait aucun
- Comme Gemma 4 26B active 8 experts par token sur 30 couches, cela conduisait à consommer environ 240 tenseurs par passe avant, remplis de valeurs résiduelles présentes dans les buffers mémoire
Les indices visibles dans une sortie incohérente
- La sortie erronée semblait fluide, mais n’était qu’une suite multilingue dénuée de sens
- les IDs de tokens étaient répartis uniformément sur l’ensemble du vocabulaire de 262 000 entrées
- thaï, coréen, sentinelles
<unused>et fragments anglais apparaissaient avec une fréquence comparable
- Avec une température à 0, la sortie était déterministe ; les exécutions mono-thread et multi-thread produisaient exactement les mêmes octets, et aucun NaN n’apparaissait
- Couche après couche, de grandes constantes poussaient les états cachés jusqu’à aplatir le softmax final
- Claude a instrumenté les logits bruts avant l’échantillonnage pour afficher les 5 meilleurs tokens, la plage, la moyenne et le nombre de NaN
- pour le premier token prédit, le logit moyen n’était pas proche de 0 mais de +16
- environ 80 % du vocabulaire présentait un logit positif
- Comme le biais restait constant, contrairement à une corruption aléatoire, l’analyse s’est resserrée sur l’hypothèse que de larges portions des états cachés utilisaient de la mémoire non initialisée, contenant de petites valeurs flottantes positives résiduelles
Une correction en trois étapes
- Le patch se compose de trois commits appliqués au-dessus de
maindu fork -
Correctifs de compilation sans AVX2
- Les branches scalaires
#elsedequantize_row_q8_0_x4etquantize_row_q8_1_x4_Tdansiqk_quantize.cppfaisaient en réalité référence à des helpers AVX2 commehsum_i32_8 - Ces branches ont été réécrites sous forme de boucles scalaires portables
- Des gardes
#if GGML_USE_IQK_MULMATont été ajoutés à certains appels IQK qui avaient fuité dansggml.cetggml-quants.c - Les includes manquants ont été ajoutés pour que
iqk_cpu_ops.cppcompile de manière autonome - Sans ces changements, le fork ne se compile pas du tout sur du matériel sans AVX2
- Les branches scalaires
-
Fallback de graphe à l’exécution
- Plutôt que de corriger le dispatcher, le constructeur de graphe a été modifié pour générer, dans ce type de build, des opérations dont le chemin de calcul existe déjà
- Dans
ggml_moe_up_gate, lorsqueGGML_USE_IQK_MULMATest désactivé, le code traite les poids fusionnésup_gate_exps - La forme du tenseur est
[n_embd, 2*n_ff, n_experts], avec la première moitié pour gate et la seconde pour up - Il est découpé en deux tranches via
ggml_view_3d ggml_mul_mat_idest exécuté sur chaque trancheggml_fused_mul_unary(gate, up, SILU)combine ensuite les deux résultats- Si les poids gate et up sont déjà séparés, le code utilise sans découpage deux appels à
ggml_mul_mat_idsuivis de l’opération fusionnée multiplication + unaire - Le même fallback a été appliqué à
ggml_fused_up_gate, utilisé dans les couches non MoE mul_mat_idrepose sur l’implémentation ggml standard etfused_mul_unaryexécute SILU et la multiplication en une seule opération ; il existait donc déjà une implémentation non IQK pour tous les composants nécessaires- L’ensemble des changements se trouve derrière
#if !GGML_USE_IQK_MULMAT, si bien que les builds AVX2 restent strictement identiques bit à bit
-
Remise en état des stubs CI
- Les stubs
#elsedes sources IQK ne correspondaient pas àiqk_mul_mat.h, de sorte queci/run.shne compilait pas non plus sur du matériel sans AVX2 - L’include
<cstdint>manquait - Certains stubs avaient un paramètre initial superflu ou omettaient le paramètre
sinks, ce qui modifiait leur signature - D’autres fonctions n’avaient pas de stub du tout, provoquant des références non définies à l’édition de liens
- Une fois les stubs réalignés sur le header, il est devenu possible d’exécuter la suite de tests en environnement non AVX2
- Les stubs
Coût en performances du fallback et bug lié au repacking
- Le fallback exécute deux
mul_mat_idau lieu d’un seul kernel fusionné, ce qui ajoute un surcoût - Ce CPU est déjà limité par la bande passante mémoire, et le kernel fusionné d’origine étant de toute façon réservé à AVX2, aucun chemin d’exécution existant n’est perdu dans cet environnement
- Les performances finales atteignent environ 5,2 tokens/s en décodage et 16 tokens/s en évaluation de prompt sur le MoE 26B-A4B
--run-time-repackréorganise au démarrage les poids quantifiés dans un format entrelacéQ8_0_R8spécifique à AVX2- Il s’agit d’un bug distinct qui recorrompt la sortie en environnement AVX1
- Le patch actuel ne le corrige pas et retire donc ce flag du script d’exécution
Comment la cause a été isolée
- L’incompatibilité de jeu d’instructions était facile à constater, mais il était plus difficile d’identifier que le dispatcher tombait silencieusement dans la branche par défaut sans signaler d’erreur
- La revue du code a permis d’éliminer plusieurs pistes
- les helpers RMSNorm semblaient corrects
- le fallback AVX1 de
ggml_vec_dot_q8_0_q8_0semblait lui aussi correct - les résultats identiques bit à bit en mono-thread ont permis d’exclure un bug de threading
- Le fait que la moyenne des logits reste figée à +16 et que des tokens de longue traîne aient des valeurs proches a conduit à conclure qu’une grande partie du flux résiduel n’était pas initialisée
- Après avoir recherché
#if GGML_USE_IQK_MULMATdans le dispatcher, les deux chemins d’opérations manquants ont été trouvés en environ une minute
Conditions de reproduction et cas d’usage
- Les conditions de reproduction sur du matériel antérieur à AVX2 sont les suivantes
- Matériel : deux Xeon E5-2690 v2, Ivy Bridge, AVX1, DDR3, sans GPU
- Build : compiler
ik_llama.cppde la branche patchée sansGGML_USE_IQK_MULMAT - Modèle : Gemma 4 26B-A4B Q8_0
- Exécution : utiliser les flags CPU habituels de
ik_llama.cpp, à l’exception de--run-time-repack
- Les changements exacts sont disponibles dans ikawrakow/ik_llama.cpp PR #2138
- Au moment de la rédaction, la PR est encore ouverte et en attente de revue par un mainteneur ; il faut donc exécuter directement la branche
- Les bugs observés sur le même type de matériel peuvent être signalés dans le fil de la PR
- Conserver un modèle local sur un ancien serveur d’entreprise peut fournir une solution de secours lorsque les API payantes tombent en panne, ou servir à traiter des tâches batch lentes pour lesquelles une facturation au token n’est pas adaptée
- L’auteur valorise davantage la capacité à creuser un code inconnu et des systèmes anciens que l’usage de services par abonnement ; la même approche s’applique aussi bien à la maintenance d’une application Rails vieille de 15 ans qu’à celle d’une base de données dont le responsable est parti
1 commentaires
Avis de Hacker News
On s’attend à ce que, d’ici la mi-2027, il soit possible de faire tourner des modèles MoE (mélange d’experts) de plus de 200 milliards de paramètres sur du matériel grand public
Je fais tourner Qwen3.6-35B-A3B en local sur un Mac 16 Go et j’obtiens 7 à 9 tokens par seconde : https://github.com/deepanwadhwa/samosa-chat
En gros, un modèle de niveau GPT-4 tourne en local à cette vitesse sur un MacBook Air avec 16 Go de RAM
On est déjà dans le même ordre de grandeur que le nombre de paramètres actifs de la plupart des modèles de plus de 200 milliards de paramètres, donc Prism pourrait sortir un tel modèle s’il le voulait. Cela dit, les réseaux neuronaux récurrents comme HRM n’ont pas besoin d’autant de paramètres, donc la nécessité réelle d’une telle échelle reste discutable : https://huggingface.co/sapientinc/HRM-Text-1B
Par exemple, si chaque couche ne contenait qu’une graine de 256 bits passée dans une fonction de bruit pour générer 16K poids réels, on pourrait descendre sous 1 bit de stockage par poids
J’ai hâte de voir les nouveaux modèles qui devraient changer la donne ce mois-ci et le mois prochain. Ornith vaut vraiment le détour
Certains n’aimeront pas l’entendre, mais je pense que le coût des tokens chez les fournisseurs d’inférence est inférieur au coût de l’électricité pour une exécution locale
En simplifiant et en ne comptant que la génération en sortie, 5 tokens par seconde font 18 000 tokens par heure, ce qui coûterait environ 0,005 dollar chez un fournisseur. En supposant que le serveur consomme environ 500 W pendant l’inférence, et avec un tarif électrique allemand de 0,3 dollar/kWh, produire la même quantité en local coûte 0,15 dollar, soit 30 fois plus cher
Si la confidentialité est une préoccupation, l’exécution locale reste intéressante, mais il faut savoir qu’elle est bien moins efficace que les fournisseurs d’inférence. Avec les nouveaux GPU qui améliorent l’efficacité de l’inférence, cet écart va fortement se creuser
Au départ, j’avais calculé à tort 180 000 tokens, mais il s’agit en réalité de 18 000 tokens ; à moins d’avoir une électricité quasiment gratuite, il est difficile de rivaliser. Les fournisseurs utilisent sans doute encore des H200/H100 pour les petits modèles, mais lorsqu’ils passeront aux GB300 ou aux GPU Ruby de l’an prochain, le coût d’inférence sera divisé par 30 et la principale valeur des modèles locaux sera probablement la protection de la vie privée
En une heure, cela permet de traiter 3,6 millions de tokens en entrée ou de générer 144 000 tokens, pour environ 0,15 dollar d’électricité. Avec Sonnet, la même quantité coûterait 7,2 dollars en entrée et 1,4 dollar en génération ; le cloud est donc 10 fois plus cher pour la génération et presque 50 fois plus cher pour le traitement
Les fournisseurs d’inférence sont lourdement endettés et se battent pour des parts de marché, donc les prix augmenteront forcément
Si l’IA est suffisamment démocratisée pour être contrôlée directement par les communautés, cela résout aussi le problème des data centers, et le niveau de censure et d’alignement peut être décidé démocratiquement par la communauté. C’est une idée proche de certains textes de Geohotz
Les modèles open source deviendront bientôt assez bons et efficaces pour être servis à bas coût sur du matériel d’occasion. Dans chaque petite communauté, il suffira qu’une personne à l’aise avec la technique réunisse quelques centaines de dollars de coût initial ; ensuite, le service pourra fonctionner presque gratuitement, sans facture d’électricité
https://solar.lowtechmagazine.com/
Sans l’option locale, les prix des modèles cloud seraient bien plus élevés
Les Xeon doubles de cette génération risquent de consommer plus de 300 W en charge. Au tarif moyen de l’électricité aux États-Unis, cela revient à 1,35 dollar par jour, et davantage s’il faut climatiser la pièce en été.
Même en ignorant le temps de traitement du prompt et en le faisant tourner 24 h/24, on obtient environ 400 000 tokens par jour, soit environ 0,30 dollar par million de tokens en sortie. Par coïncidence, c’est le prix actuel de ce modèle sur OpenRouter, mais la vitesse de génération d’OpenRouter est 8 fois plus rapide.
Il y a beaucoup de raisons d’expérimenter avec un LLM local, comme éviter que les données quittent la maison, mais il est difficile d’y gagner financièrement. Ayant moi-même investi bien plus d’argent dans du matériel d’inférence local à la maison, je trouve ça amusant, mais pas vraiment un moyen de faire des économies.
J’ai résumé les résultats obtenus en faisant tourner plusieurs modèles sans GPU sur un double Xeon avec 256 Go de DDR4.
https://gist.github.com/hparadiz/f3596d00a62d8ebb2dadcc46ee5822c7
C’est plutôt lent. Même sur un CPU de 13 ans, j’obtiens 8 à 12 tokens par seconde, selon la taille du contexte et d’autres réglages.
https://news.ycombinator.com/item?id=48354801
Je suis l’auteur du billet original. Le commentaire d’origine semble avoir été signalé pour une raison quelconque. J’ai ouvert les modifications sous la forme de la PR #2138 du projet amont : https://github.com/ikawrakow/ik_llama.cpp/pull/2138
Sur cette machine StoreVirtual, il ne semble pas y avoir de port vraiment utilisable pour installer des logiciels. Il n’y a guère que des ports USB ; je me demande si l’installation se fait via une console série.
Avec la même configuration, je fais tourner Gemma 4 26B sur un Mac Pro de 2013 et j’obtiens environ 5 tokens par seconde. Les deux cartes graphiques ne servent à rien ici, mais pour certaines tâches c’est suffisamment pratique.
Il arrive vendredi, et j’ai hâte de tester ça moi-même.
https://echalupa.com/blog/mac-pro-6-1-llama-cpp-firepro-d300-vulkan-ubuntu
https://matthewgribben.com/blog/mac-pro-6-1-llama-cpp-firepro-d700-vulkan-ubuntu
J’ai vu de mes propres yeux la configuration dans le sous-sol, et elle est vraiment impressionnante. La prochaine fois, j’aimerais bien voir aussi l’imprimante 3D.
Une vidéo assez liée : quelqu’un y fait tourner un LLM sur un Pentium 4 et l’a surnommé NetburstGPT. Évidemment, c’est très lent.
https://www.youtube.com/watch?v=ILV-eu90te8