Exécution d'un LLM de 2,78 billions de paramètres sur un seul CPU avec 8 Go de RAM : à l'intérieur du moteur C Kimi K3

Découvrez comment un binaire C99 de 176 Ko exécute un modèle Kimi K3 de 2,78 billions de paramètres sur un seul CPU avec seulement 8,24 Go de RAM, en utilisant le streaming, MXFP4 et une gestion intelligente de la mémoire.

Imaginez exécuter un modèle de langage de 2,78 billions de paramètres sur votre ordinateur portable. Pas une version quantifiée, distillée ou autrement compromise—le modèle complet, original Kimi K3, avec 1,56 To de poids, produisant une sortie octet pour octet identique à celle que vous obtiendriez d'un cluster de GPU en datacenter. C'est exactement ce que le projet kimi-k3-in-c réalise, et c'est une leçon magistrale d'ingénierie système.

Ce n'est pas une démo jouet. C'est un moteur d'inférence entièrement fonctionnel écrit en C99 portable, sans BLAS, sans framework d'apprentissage profond, et sans support GPU. Le moteur entier compile en un binaire de 176 Ko. Le secret ? Une combinaison d'exploitation de l'architecture du modèle, de streaming mémoire agressif, et d'une compréhension profonde de l'endroit où vit chaque octet.

Le problème : un modèle qui ne tient pas

Kimi K3 a 2,78 billions de paramètres, ce qui en précision bfloat16 nécessiterait 5,56 To de mémoire. Le point de contrôle publié est de 1,56 To, toujours bien au-delà de toute machine grand public. Mais Kimi K3 est un modèle de mélange d'experts (MoE) : pour chaque jeton, seulement 16 des 896 experts par couche sont actifs. Cela représente environ 104 milliards de paramètres actifs—seulement 3,7 % du total.

L'idée clé : les autres 96,3 % des paramètres n'ont pas besoin d'être en RAM. Ils ont juste besoin d'être accessibles. En diffusant les experts inactifs depuis le disque à la demande, l'exigence de mémoire diminue considérablement.

Les quatre réductions

Le projet réalise une réduction de mémoire de 675x par rapport à la ligne de base théorique bfloat16 grâce à quatre étapes clés :

  1. Quantification MXFP4 : Les experts routés sont déjà stockés au format MXFP4—un format de flottant 4 bits à micro-échelle. Chaque poids est un nibble de 4 bits, avec un exposant partagé de 8 bits pour 32 poids. Cela réduit le stockage des experts de 5,45 To à 1,447 To.

  2. Sparsité : Seulement 16 des 896 experts se déclenchent par jeton, donc les 1,447 To d'experts n'ont jamais besoin d'être entièrement résidents. Ils sont diffusés depuis le disque selon les besoins.

  3. Attention KDA : 69 des 93 couches utilisent l'attention delta de Kimi, un mécanisme d'attention récurrent avec un état de taille fixe qui ne croît pas avec la longueur du contexte. Cela élimine l'explosion du cache KV pour la plupart des couches.

  4. Compression MLA : Les 24 couches restantes utilisent l'attention latente multi-têtes, qui met en cache un seul latent de dimension 576 par position au lieu de paires clé/valeur par tête. Cela réduit la taille du cache KV de 53x.

Après ces réductions, l'ensemble toujours résident est de seulement 113,49 Go (le tronc dense, les plongements et la tête de sortie). Mais c'est encore trop pour un ordinateur portable. La réduction finale : diffuser le tronc lui-même.

Diffusion du tronc : un cadran, pas un plancher

Le tronc (93 couches denses) fait 108,81 Go. Chaque couche est utilisée pour chaque jeton, donc il n'y a pas de sparsité à exploiter. La solution est d'épingler autant de couches en RAM que votre budget le permet, et de diffuser le reste depuis le disque à travers un tampon en anneau unique.

Le moteur parcourt les couches 0 à 92 dans l'ordre à chaque jeton. C'est un balayage cyclique, ce qui est le pire scénario pour la mise en cache LRU. Donc, au lieu d'un cache, il utilise un préfixe épinglé : les N premières couches sont résidentes en permanence, et le reste cycle à travers un emplacement en anneau. Cela donne un taux de succès déterministe de N/93.

Par exemple, avec un --preset laptop (budget de tronc de 3 Go), seulement 10 couches sont épinglées, et le reste est diffusé depuis le disque. Le résultat : un pic RSS de 8,24 Go, mais 26,5 secondes par jeton. Avec --preset server (budget de tronc de 110 Go), 90 couches sont épinglées, et la vitesse passe à 5,6 secondes par jeton.

Le cache d'experts : une leçon de mesure

Le moteur inclut également un cache LRU pour les experts routés. Mais voici la partie surprenante : le cache est presque inutile aux petites tailles. Les mesures du projet montrent que l'augmentation du cache de 28 emplacements à 1 344 emplacements (une augmentation de 48x) change les octets lus par jeton de exactement zéro.

Pourquoi ? Parce que Kimi K3 utilise une technique appelée équilibrage quantile, qui aplatit l'utilisation des experts à travers le pool. Sans sous-ensemble chaud d'experts, un cache LRU ne retient rien d'utile. Le cache ne commence à aider que lorsqu'il est suffisamment grand pour contenir une fraction significative de l'ensemble de travail (environ 36 Go d'arène).

Cela conduit à une optimisation contre-intuitive : donnez d'abord de la mémoire au tronc, pas au cache d'experts. Avec un budget fixe de 128 Go, allouer 110 Go au tronc et 13 Go au cache est 1,69x plus rapide que la répartition inverse, même si cette dernière a un taux de succès de cache plus élevé.

Reproductibilité bit à bit

L'un des aspects les plus impressionnants est l'engagement envers la reproductibilité bit à bit. Le moteur garantit que les chemins de code scalaire, OpenMP et AVX2 produisent tous des résultats identiques. Cela est réalisé en :

  • Utilisant -ffp-contract=off pour empêcher la contraction FMA, qui changerait l'arrondi.
  • Accumulant en double précision avec un ordre de sommation fixe.
  • Élargissant bf16 en fp32 via un simple décalage (sans perte).

Cela signifie que la sortie est octet pour octet identique, que vous exécutiez sur un ordinateur portable de 8 Go ou une station de travail de 224 Go. La seule différence est la vitesse.

Validation : prouver que cela fonctionne

Le projet comprend une suite de validation rigoureuse :

  • Tests sans poids : Un modèle oracle de 13 couches avec le même graphe tensoriel que le modèle complet, vérifié contre une référence PyTorch. Toutes les portes passent exactement.
  • Conformité du point de contrôle complet : Les 93 couches vérifiées contre PyTorch, avec une erreur dans le pire cas de 0,00x la tolérance autorisée.
  • Parité des logits : Les logits du moteur C correspondent à la référence PyTorch élément par élément, avec une différence maximale de 7,87e-6.
  • Échelle de mémoire : 12 budgets de mémoire différents de 8 Go à 224 Go, produisant tous des identifiants de jeton identiques.

Le résultat final

Ce projet est un témoignage de ce qui est possible avec une ingénierie système minutieuse. Il prouve qu'un modèle de mille milliards de paramètres ne nécessite pas un datacenter—juste une compréhension intelligente de l'endroit où vivent les octets et comment les déplacer efficacement.

Que vous soyez intéressé par les architectures MoE, l'inférence économe en mémoire, ou que vous vouliez simplement voir du code C impressionnant, kimi-k3-in-c vaut le détour. Le README seul est un trésor de détails techniques, et le code est propre et bien documenté.

Si vous êtes inspiré pour l'essayer vous-même, vous aurez besoin d'environ 1,7 To d'espace disque libre et de beaucoup de patience pour le téléchargement. Mais la récompense est d'exécuter l'un des plus grands modèles ouverts sur du matériel que vous possédez probablement déjà.

Source

FareedKhan-dev/kimi-k3-in-c : Un Kimi K3 de 2,78 billions de paramètres exécutant l'inférence sur un seul CPU dans 8,24 Go de RAM. C99 portable : pas de BLAS, pas de framework, pas de GPU.