- WASTE est un moteur d’inférence en C qui convertit le modèle open weights complet Kimi K3 de 2,78 billions de paramètres, sans réduction, en un conteneur de 982 Gio exécutable sur un ordinateur portable grand public
- Il garde uniquement le tronc résident du modèle en mémoire et lit depuis le NVMe environ 4 % des poids d’experts activés à chaque token, le reste de la RAM servant de cache d’experts à taille limitée
- Kimi K3 peut être ouvert avec un minimum de 29,05 Go de RAM pour un contexte 4K, mais la configuration pratique est un budget de 46 Go sur un MacBook Pro 64 Go, où il atteint 0,45 à 0,62 tok/s
- En chevauchant lectures d’experts et calculs, il obtient environ 1,6× d’amélioration, et en exécutant le routeur de la couche suivante un residual en avance, il fait passer le taux de cache hit de 14 % à 38 %, sans modifier le volume total lu ni les logits
- Il permet d’exécuter localement un très grand modèle sans connexion Internet, sans coût par token ni transfert de données externe, mais nécessite un NVMe interne et environ 1 To de stockage ; avec un budget RAM de 52 Go ou plus, le paging de l’OS peut au contraire provoquer un fort ralentissement
Objectif et forme d’implémentation de WASTE
- WASTE (Weight-Aware Streaming Tensor Engine) est un moteur d’inférence C intégrable, sans dépendance externe à l’exécution
- Il n’utilise que
libwaste.aet l’exécutablewaste, et ne nécessite ni BLAS, ni CUDA, ni ONNX, ni Python en dehors de libc et pthreads - Python n’est utilisé que pour la conversion du modèle et la validation par rapport à PyTorch, pas dans le chemin d’inférence
- L’API publique comprend 26 fonctions et prend en charge l’ouverture du modèle, le réglage d’une limite de RAM, la génération, la sauvegarde de session et l’arrêt
- Il n’utilise que
- La cible actuellement validée est le modèle complet Kimi K3 2.78T
- La source publique fait 1,42 To et le conteneur après conversion fait 982 Gio
- Ce n’est ni une version distillée, ni élaguée, ni réduite
- Kimi-Linear 48B atteint aussi, avec le même moteur et le même format, un conteneur de 19 Gio, un minimum de 1,87 Go de RAM et 10,7 tok/s
- Le nom du projet vient de l’objectif de réduire les situations où l’on exécute dans des datacenters cloud, en consommant à la fois des coûts de tokens et de l’électricité, des modèles qui pourraient tourner sur du matériel de bureau
Architecture de streaming depuis le disque
- K3 étant une architecture Mixture of Experts, seulement environ 4 % du modèle sont activés par token ; les poids inactifs peuvent donc être disposés pour être accessibles au moment nécessaire, sans rester en RAM
- Le conteneur
.wastese compose d’un manifeste JSON, d’un tronc résident et de banques d’experts par couche- Chaque enregistrement d’expert est aligné sur 4 KiB
- Les matrices gate, up et down sont placées de manière contiguë afin qu’un expert soit lu exactement en un seul
pread - Le cache de pages est contourné via
F_NOCACHEsur macOS,O_DIRECTsur Linux etFILE_FLAG_NO_BUFFERINGsur Windows
- Si le cache de pages n’est pas contourné, un conteneur de test plus petit que la RAM peut entrer dans le cache de l’OS et produire des taux de hit qui ne se reproduisent pas avec le modèle de 982 Gio
- Lors de la lecture des enregistrements, le magic, l’ID d’expert et la plage d’offsets sont toujours vérifiés pour éviter qu’une banque tronquée ou mal assemblée ne réponde avec les mauvais poids
- La vérification
crc32du payload s’active avec--verify - Le coût de vérification est d’environ 5 % sur Kimi-Linear et d’environ 1 % sur K3 ; elle est désactivée par défaut
- Il est recommandé de vérifier une fois les conteneurs copiés, téléchargés ou placés sur un disque non fiable
- Le tronc et les codebooks n’ont pas de checksum
- La vérification
Lecture anticipée et prédiction du routeur
- Quand le routeur d’une couche décide de 16 ID d’experts, chaque lecture est demandée dans un thread distinct, et le calcul consomme les données dès leur arrivée
- Le chevauchement des lectures et des calculs améliore K3 d’environ 1,6×
- Le travail effectué et les statistiques de cache sont les mêmes avant et après l’activation de la fonctionnalité
- Avant que le hidden state réel de la couche suivante ne soit produit, le routeur suivant résident est exécuté sur le hidden state actuel pour précharger 6 experts
- Cette prédiction un residual en avance est correcte à 92 % au rang 1 et à 81 % dans le top 6
- Le routeur réel décidant des experts finaux, la sortie reste exactement correcte
- Le demand hit rate passe de 14 % à 38 %, tandis que le nombre total d’octets lus ne change pas
- Elle peut être désactivée avec
WASTE_LOOKAHEAD=0
- L’implémentation qui appliquait la même technique au prefill a été supprimée
- Une couche decode occupe 16 slots de cache, alors qu’une couche chunk en occupe environ 550
- Les enregistrements lus à l’avance étaient évincés avant utilisation, augmentant le volume lu de 6,9 % sans réduire le temps
Quantification et exactitude
- Les poids d’experts sont stockés avec une quantification vectorielle résiduelle en 3 étapes, avec des codebooks de 256 entrées pour des vecteurs de dimension 8, soit 3,00 bits par poids
- Sans reconstruire toute la matrice, une table de produits scalaires partiels est construite, puis chaque ligne est traitée avec 3 recherches dans la table et 2 additions
- Le tronc conserve des poids 4 bits et 8 bits
- Comme le modèle n’a été entraîné avec prise en compte de la quantification que pour les experts, la sortie s’effondre avec un tronc en 3 bits
- La prédiction de cache était correcte, mais le débit ne s’améliorait pas non plus, elle a donc été supprimée
- Toutes les couches sont comparées à l’implémentation de référence PyTorch
- L’écart final sur les logits est de
3.6e-06 - La tour vision est à
2.3e-06de sa propre référence - La conversion du cache KV latent conserve des logits identiques à un niveau de
1.2e-05
- L’écart final sur les logits est de
Budget RAM et fenêtre de performance étroite
- K3 utilise 16 experts dans chacune de ses 92 couches, créant un working set de 17,0 Go par token
- Si le cache est plus petit que cette taille, les experts stockés pendant un token sont évincés avant le token suivant et le taux de hit devient 0 %
- Les mesures sur un système 64 Go montrent qu’allouer plus de RAM n’accélère pas toujours
- Budget 32 Go, cache 3,32 Go : taux de hit 0 %, 0,50 tok/s
- Budget 46 Go, cache 17,32 Go : taux de hit classique 17 %, 0,53 à 0,55 tok/s
- Budget 52 Go, cache 23,32 Go : non reproductible, 0,04 à 0,15 tok/s
- Budget 58 Go, cache 29,32 Go : 0,02 à 0,03 tok/s
- Le lookahead du routeur fait passer le taux de hit d’environ 14 % à 38 % avec 46 Go, mais l’effondrement au-delà de 52 Go est dû au paging de l’OS, pas aux cache misses
- À 58 Go, malgré un taux de hit plus élevé, c’est environ 20 fois plus lent qu’à 46 Go
- Après avoir mis le système en situation de paging avec un gros budget, même les mesures à 46 Go peuvent tomber à 0,02 tok/s
- Le budget par défaut est choisi sous les 7/8 de la RAM physique, arrondi vers le bas par unité de working set de token
- Sur un MacBook Pro 64 Go, il utilise 46,24 Go et alloue 17,56 Go au cache d’experts
- Si le budget explicite est inférieur au minimum, il refuse de démarrer au lieu de continuer en swap
- Sur un système 128 Go, on peut utiliser le budget total recommandé correspondant au tronc plus 3 fois le working set
Performances de K3 et exigences matérielles
- Le système de mesure est un MacBook Pro M5 Pro 64 Go avec SSD interne
- RAM minimale pour un contexte 4K : 29,05 Go
- 32K : 30,54 Go, 128K : 35,63 Go, 1M : 83,21 Go
- Tronc résident : 27,28 Go
- Chargement du modèle : 20 secondes
- Decode : 0,45 à 0,62 tok/s avec le budget par défaut
- Prefill : 0,47 tok/s en chunked, 0,29 tok/s en séquentiel
- Même s’il est possible d’ouvrir le modèle avec un minimum de 29,05 Go, un système 32 Go peut paginer sévèrement ; 64 Go est donc la configuration réellement recommandée
- À froid, 17,0 Go d’experts sont lus par token ; avec un taux de hit de 38 % grâce au lookahead, 10,5 Go sont lus
- Le SSD interne est mesuré à 12,78 Go/s, contre 0,94 Go/s pour un boîtier USB externe
- Comme un token lit 17 Go d’experts, le même traitement prend environ 13 secondes sur un stockage externe
- Le téléchargement d’origine peut être placé sur un disque externe, mais le conteneur converti doit être sur le NVMe interne
- Il faut 982 Gio pour le conteneur converti et 1,42 To pour le staging des shards d’origine ; l’espace de staging peut être libéré après conversion
Attention et traitement multimodal
- L’attention de K3 combine Kimi Delta Attention et gated multi-head latent attention dans un ratio 3:1
- KDA maintient un état récurrent de taille fixe au lieu d’un cache KV croissant
- MLA met en cache un latent de largeur 512 sans étendre les keys/values par tête
- En absorbant
kv_b_projdans la query et l’output, le cache pour un contexte 4K passe de 11,25 Go à 0,21 Go- C’est une réduction de 53× par rapport à l’approche précédente
- En 128K, le layout étendu demanderait 360 Go, contre 7,2 Go pour le layout latent
- Le chemin multimodal prend en charge un ViT de 401 M de paramètres, 27 couches et des patches de 14
- L’encodage d’une image de 1024 patches prend 15,7 secondes
- Une image 896×896 occupe 256 positions de séquence avec les réglages par défaut
- Comme les embeddings d’image passent aussi par les 92 couches MoE, la majeure partie du coût est similaire au text prefill, plus qu’à la tour vision
- Réduire de moitié
max_patchesdansvision.jsonréduit aussi de moitié le nombre de positions de prompt
- PNG, JPEG, GIF, BMP, TGA et PSD sont pris en charge, et les images peuvent être utilisées avec
run,chateteval- Dans une conversation, les positions des images encodées restent dans l’état d’attention et ne sont pas réencodées au tour suivant
- La tour vision n’est chargée que lorsqu’il y a une image ; elle utilise 434 Mo de poids et 1,12 Go de mémoire réservée au total
Conversion, exécution et serveur
- La compilation ne nécessite qu’un compilateur C11 et
makemake checkpasse 23 tests et en saute 11 avec un conteneur synthétique, sans modèle réel- Avec les deux conteneurs réels, la suite complète compte 36 tests
- La conversion de K3 utilise directement les 96 shards safetensors publics de moonshotai/Kimi-K3
- Elle prend environ 4,7 heures sur M5 Pro avec 3 processus
- L’encoder PyTorch pur prend 23,7 heures
- Elle peut reprendre couche par couche ; en cas d’interruption, seule la couche en cours est retraitée
- Le downloader prend en charge la reprise des fichiers partiels, l’exponential backoff avec jitter, la vérification de
Content-Lengthet l’enregistrement de l’état des shards terminés
- La CLI fournit notamment
run,chat,evaletplan, et--jsonpermet de produire des résultats lisibles par machine poureval,tokenize,plan,infoetbench serve/est un serveur HTTP compatible OpenAI qui appelle l’API C publique via ctypes- Il fournit
/v1/chat/completions,/v1/completions,/v1/modelset/health - Il gère le streaming, les définitions et résultats d’outils, les arguments d’appel typés, les schémas de réponse JSON,
tool_choice, le think channel,thinking_effortet les images - Le renderer de prompts est un portage de
encoding_k3.pyde la release K3 ; si un répertoire de poids est présent, 38 conversations sont comparées segment par segment
- Il fournit
Plateformes et limites actuelles
- macOS arm64, Linux arm64 et Linux x86_64 enregistrent 23 pass et 11 skip sur les mêmes tests indépendants du modèle, et passent aussi les sanitizers et 400 fuzz tests
- Windows x86_64 a été compilé en cross-compilation avec MinGW-w64 et validé sur un conteneur synthétique, la CLI et un forward pass, mais pas exécuté avec un vrai conteneur de modèle
- MSVC et Windows ARM64 ne sont pas pris en charge
- Le contournement du page cache sous Windows n’a été confirmé que sur le système de fichiers de la CI, pas sous la charge d’un vrai conteneur plus grand que la RAM
- Le SIMD x86 choisit AVX-512 ou AVX2 selon CPUID, mais le chemin AVX-512 n’a pas encore été exécuté sur un CPU réellement compatible
- Le backend Metal est exact, mais il est 22 % plus lent que le CPU et désactivé par défaut en raison de la forme de la charge de travail, qui déclenche des centaines de petits matvec dépendants
- L’API n’est pas encore stabilisée, et la conversion automatique du chat format ne prend actuellement en charge que K3
- Kimi-Linear s’exécute en mode raw sans essayer de deviner le template
- L’allocation non uniforme de bits par expert ne sera pas introduite
- La valeur du troisième bit ne varie que jusqu’à 1,15× entre experts d’une même couche et 1,01× entre couches, ce qui n’apporte pas de gain avec une allocation optimale
- L’allocation fondée sur la fréquence de routing réduit aussi l’espace de stockage, mais réduit très peu l’I/O, qui est le goulot d’étranglement
- La licence est Apache 2.0
1 commentaires
Commentaires sur Hacker News
Vraiment impressionnant. Ce n’est pas un projet qui cherche à être plus pratique que les fournisseurs cloud dès maintenant, mais un projet qui montre les limites du possible
Si les gains d’efficacité des modèles et les progrès du matériel local se rejoignent, il pourrait devenir économiquement viable de faire tourner un jour des modèles locaux de haute qualité
0,5 token par seconde, ça me semble inutile pour des tâches longues. Je préférerais dépenser l’argent pour deux 4060 Ti 16 Go avec parallélisme tensoriel
Dans 20 ans, ça conviendrait peut-être à des robots lents au style cyberpunk, alimentés au solaire, qui tondent la pelouse ou nettoient les trottoirs, ou à un robot de jardin qui suit tout juste la croissance d’un bonsaï pour le tailler
Dire qu’il est absurde de payer le coût au token pendant que le fournisseur d’inférence paie l’électricité, je ne vois pas en quoi c’est différent du fait d’acheter des concombres alors que l’agriculteur paie l’eau et l’engrais. J’espère que cette logique plaquée après coup ne s’applique qu’aux LLM
L’idée en elle-même est intéressante, et j’aimerais l’essayer avec un plus petit modèle. Si on génère 0,5 token par seconde tout en lisant plusieurs Go par seconde depuis le SSD, c’est encore trop gros pour un portable grand public, mais ça pourrait au contraire devenir pratique pour des modèles de 250 à 500 Gio
En supposant une consommation continue de 42 W et un prix de l’électricité de 0,20 $/kWh, cela fait environ 5 dollars par million de tokens, hors autres coûts comme le matériel
Le llama.cpp standard peut aussi
mmapdes GGUF, donc ce qui ne tient pas en mémoire reste sur disque, et le cache de pages du noyau conserve les chunks résidents les plus utilisés. Je me demande quel est l’avantage d’une implémentation maisonmmap, puis ont fait leur propre implémentation et c’était 10 fois plus rapideC’est la même raison pour laquelle les moteurs de base de données implémentent leur propre cache. La pagination du noyau est généraliste et pilotée par la demande, mais si on connaît les vrais schémas d’accès, on peut précharger les données nécessaires et les pipeline
Pour un modèle qui tient entièrement en RAM, il valait mieux lancer llama-server avec
--no-mmap. Bien sûr, pour charger l’ensemble de Kimi K3 et un contexte d’un million de tokens, il faudrait un serveur de 2 ToLe README donne fortement l’impression d’avoir été écrit par un LLM, et je me demande si la base de code aussi a été écrite par un LLM
Maintenant, j’utilise mes compétences pour orchestrer des LLM et des agents afin d’écrire bien meilleur code, beaucoup plus vite. Les développeurs doivent soit s’adapter aux nouvelles technologies, soit disparaître
On y retrouve tel quel des décisions internes importantes pour l’utilisateur mais sans intérêt pour quelqu’un qui regarde seulement le résultat final, ainsi que le jargon obscur propre à Claude. J’utilise souvent les LLM et je reconnais qu’ils sont très utiles pour écrire du code complexe, mais la qualité des premiers jets rédigés est médiocre
claudedans la liste des contributeurs, donc inutile de spéculer. Si on va jusqu’à laisser Claude faire les commits, il semble peu probable que le code ait été relu manuellementCela pourrait gagner en valeur si la technologie progresse au point de permettre de choisir précisément le bon modèle pour la bonne tâche. On peut imaginer un futur où, dans un processus d’exploration automatique, on n’active un gros modèle qu’une trentaine de minutes par jour, et on utilise des petits modèles le reste du temps