Hypura – ordonnanceur d’inférence LLM conscient de la hiérarchie de stockage pour Apple Silicon
(github.com/t8)- Optimise le placement des tenseurs entre GPU, RAM et NVMe pour exécuter de grands modèles de langage grâce à un ordonnanceur d’inférence conscient de la hiérarchie de stockage
- Permet d’exécuter sur un Mac Mini 32 Go le modèle Mixtral 8x7B (31GB) à 2.2 tok/s et le modèle Llama 70B (40GB) à 0.3 tok/s
- Analyse les schémas d’accès et la bande passante matérielle pour faire tourner de manière stable même des modèles dépassant la mémoire physique, y compris des modèles que
llama.cppne pouvait pas traiter à cause d’un OOM - Grâce au routage des experts pour l’architecture MoE, au cache de neurones et au prefetch, réduit les E/S jusqu’à 75 % et atteint un taux de cache hit de 99.5 %
- Sélectionne automatiquement les modes Full-resident, Expert-streaming et Dense FFN-streaming selon la taille du modèle et le matériel afin de maintenir les meilleures performances
- Fournit une API HTTP compatible Ollama, intégrable avec OpenClaw et autres outils, et utilise le SSD en lecture seule pour permettre une inférence sur NVMe sans dégradation de durée de vie
Vue d’ensemble
- Hypura est un ordonnanceur d’inférence LLM conscient de la hiérarchie de stockage pour Apple Silicon, un outil qui effectue une optimisation du placement des tenseurs entre GPU, RAM et NVMe
- En répartissant les tenseurs selon les schémas d’accès, le coût en bande passante et les performances matérielles, il permet d’exécuter de façon stable de grands modèles dépassant la mémoire physique
- Sur un Mac Mini 32 Go, il permet d’exécuter Mixtral 8x7B (31GB) à 2.2 tok/s et Llama 70B (40GB) à 0.3 tok/s
- Dans le même environnement, llama.cpp ne peut pas s’exécuter à cause d’un OOM (Out of Memory)
Contexte du problème
- Les Mac grand public disposent d’une mémoire unifiée rapide et d’un stockage NVMe, mais leur capacité mémoire reste limitée
- Par exemple, un M1 Max 32 Go ne peut pas charger directement un modèle de 40 Go, ce qui provoque un swap excessif et une fin d’exécution par OOM
- Hypura résout ce problème en analysant la structure du modèle pour effectuer un placement optimal par couche
Placement par couche fondé sur la structure du modèle
- Norms et Embeddings : petits, mais consultés à chaque token, donc fixés sur le GPU
- Routage des experts MoE : exploite la parcimonie, avec seulement 2 experts actifs sur 8 par token
- Intercepte le routeur pour identifier les experts actifs, puis charge depuis le NVMe uniquement les parties nécessaires
- Réduction des E/S de 75 % et taux de hit du cache de neurones de 99.5 %
- Effectue un prefetch anticipé des prochains experts actifs via le suivi de co-activation (co-activation tracking)
- Poids FFN denses : représentent environ 60 % de la taille du modèle
- Streaming depuis le NVMe via un pool de buffers dynamique
- La profondeur d’anticipation du prefetch (prefetch lookahead depth) est ajustée automatiquement selon la mémoire disponible
- Résultat : des modèles qui plantaient avec l’approche
mmapclassique peuvent désormais être exécutés, tandis que les modèles tenant en mémoire fonctionnent à la vitesse du GPU Metal sans surcoût
Fonctionnement
- Hypura lit les fichiers GGUF et profile la bande passante GPU, RAM et NVMe
- Chaque tenseur est placé dans l’une des trois couches suivantes
- GPU (Metal) : couches Attention, Norm et Embedding
- RAM : couches de débordement qui ne peuvent pas être chargées sur le GPU
- NVMe : couches restantes, avec E/S directes via
F_NOCACHE+pread
- Sélectionne automatiquement le mode d’inférence selon la taille du modèle et le matériel
- Full-resident : charge l’ensemble du modèle en GPU+RAM, sans E/S NVMe
- Expert-streaming : pour les modèles MoE, seuls les tenseurs non experts résident sur le GPU, les tenseurs experts sont streamés depuis le NVMe
- Dense FFN-streaming : pour les grands modèles non-MoE, Attention+Norm sur le GPU, FFN streamé depuis le NVMe
- La taille du pool de buffers, la profondeur de prefetch et le budget mémoire sont calculés automatiquement selon le profil matériel
Performances
- Environnement de test : M1 Max, 32 Go de mémoire unifiée, NVMe 5.1GB/s
- Principaux résultats de benchmark
- Qwen 2.5 14B Q4_K_M (8.4GB) : entièrement chargé sur le GPU, 21 tok/s
- Mixtral 8x7B Q5_K_M (30.9GB) : mode Expert-streaming, 2.2 tok/s, 99.5 % de taux de hit du cache
- Llama 3.3 70B Q4_K_M (39.6GB) : mode Dense FFN-streaming, pool de 24 slots, prefetch sur 7 couches, 0.3 tok/s
- Les modèles qui tiennent en mémoire ont 0 surcoût, et les modèles plus volumineux restent exécutables grâce à Hypura
Installation et exécution
- Rust 1.75+ et CMake requis
- Procédure d’installation
git clone --recurse-submodules https://github.com/hypura/hypura.git cd hypura cargo build --release - Exemples d’exécution
hypura profile hypura run ./model.gguf --prompt "Hello, world" hypura run ./model.gguf --interactive hypura bench ./model.gguf hypura inspect ./model.gguf - Pour les modèles non vérifiés, il est recommandé de tester avec
--max-tokens 10
Serveur compatible Ollama
- Hypura fournit une API HTTP compatible Ollama, entièrement compatible avec OpenClaw et les autres outils basés sur Ollama
hypura serve ./model.gguf Endpoint: http://127.0.0.1:8080 API: /api/generate, /api/chat, /api/tags - Principaux endpoints
Endpoint Fonction GET /Vérification de l’état GET /api/tagsListe des modèles chargés GET /api/versionVersion du serveur POST /api/showMétadonnées du modèle POST /api/generateGénération de texte POST /api/chatGénération conversationnelle - Pour l’intégration avec OpenClaw, définissez l’URL de base Ollama sur Hypura dans
~/.openclaw/openclaw.json - Options du serveur
hypura serve [OPTIONS] --host défaut 127.0.0.1 --port défaut 8080 --context défaut 4096
Architecture
- Structure en workspace Cargo, composée de deux crates
hypura: binaire principal et bibliothèquehypura-sys: bindings FFI pour llama.cpp (build CMake)
- Principaux modules
Module Rôle scheduler/placement.rsOptimisation du placement des tenseurs entre GPU/RAM/NVMe compute/inference.rsMoteur d’inférence et fonctions de chargement/génération pour le serveur compute/nvme_backend.rsStreaming NVMe, cache de neurones, callback d’évaluation server/routes.rsHandlers HTTP compatibles Ollama profiler/Profilage matériel cli/bench.rsOutil de benchmark model/tensor_role.rsClassification du rôle des tenseurs
FAQ
-
Aucun problème de durée de vie du SSD
- Hypura ne fait que lire sur le SSD, sans écriture
- Les E/S NVMe sont effectuées en lecture seule via
pread()+F_NOCACHE - Le SSD ne sert que de stockage à froid, les calculs étant réalisés en RAM/GPU
- Les écritures se limitent à un niveau négligeable de quelques Ko, comme les résultats JSON des benchmarks ou les fichiers de statistiques
Consignes de sécurité
- Si le modèle dépasse la limite de RAM (avec 4 Go de marge),
bench --baselineest bloqué - Pour les modèles non vérifiés, tester avec
--max-tokens 10 - Les modèles de test sont stockés dans le répertoire
./test-models/
Licence
- MIT License
Avis éthique
- Le code du dépôt n’a pas été entièrement écrit directement par son auteur
- Il a été produit dans le cadre d’une expérience de génération de code guidée par instructions avec un LLM
- Il s’agit d’un projet visant à explorer le potentiel de l’inférence fondée sur le NVMe
1 commentaires
Commentaires sur Hacker News
J’aimerais le suggérer au mainteneur. Le tableau comparatif inclut actuellement des modèles anciens comme Qwen 2.5 14B, Mixtral 8x7B et Llama 3.3 70B
Récemment, de nombreux retours indiquent que les modèles Qwen 3.5 MoE affichent des performances remarquables sur le matériel Apple
Il serait utile de consulter l’article de Simon Willison
Si possible, ce serait bien d’ajouter aussi au tableau le modèle Kimi K2.5 (1T paramètres)
Tweets associés : seikixtc, danpacary
Un message d’erreur lié à Heroku s’affichait, mais tout fonctionne de nouveau maintenant
J’y étais allé pour lire cet article, et j’ai vu que vous aviez déjà publié aussi un billet sur litellm. Lecture très intéressante
En local, même une vitesse inférieure à 1 token par seconde peut être tout à fait exploitable s’il s’agit d’un travail en arrière-plan
La différence entre « terminé immédiatement » et « terminé dans la nuit » reste malgré tout un saut de performance significatif
En pratique, ce qui compte, c’est à quel point le schéma de lecture est séquentiel
Le NVMe atteint 5 à 7 Go/s en lecture séquentielle, mais tombe autour de 500 Mo/s en lecture aléatoire
Pour un modèle 1T, il faut streamer 2 To par forward pass en fp16, donc en théorie cela prend plus de 300 secondes par token
Ce n’est pas adapté à l’interactif, mais cela peut avoir du potentiel pour la batch inference
Mais avec de petits modèles MoE, on peut générer plusieurs tokens par seconde, ce qui le rend réellement exploitable
On ne lit pas la totalité des 2 To, on n’accède qu’à une partie des couches d’experts
Chaque couche ne fait que quelques Mio, donc l’efficacité d’accès NVMe n’est pas si mauvaise
Je me demandais d’où sortait ce « modèle à 1T paramètres ». Dans le dépôt, je ne vois que des modèles de 70B ou moins
Les modèles réalistes sont plutôt de petits MoE capables de générer plusieurs tokens par seconde
Le point clé du MoE, c’est justement qu’avec l’activation sparse, on ne lit pas les 2 To entiers
Mais le schéma d’accès devient randomisé, ce qui est la pire condition possible pour du NVMe
Pour des tâches où la latence est importante, comme l’inférence d’agents, c’est l’élément central
On dirait qu’Intel Optane s’agite dans sa tombe
En pratique, ce n’est toutefois pas plus rapide que du NVMe. Avec des logiciels prenant en charge la lecture/écriture parallèle, il n’y a presque aucune différence
Cela dit, en en mettant 4 en RAID 0, on pourrait probablement saturer la bande passante d’un PCIe 16x
Le matériel Mac grand public offre une mémoire unifiée rapide et du NVMe, mais avec une capacité limitée
Si on charge un modèle de 40 Go sur un M1 Max de 32 Go, le swap s’emballe puis le système finit en panic
macOS n’a pas d’OOM killer comme Linux, il manque simplement d’espace de swap
Avoir « le plus de mémoire possible » est important, mais la bande passante est encore plus déterminante
Le M4 Pro offre 273 Go/s, le M4 Max 546 Go/s, le M4 Ultra 819 Go/s
Une fois le modèle chargé en mémoire, c’est la bande passante qui détermine la vitesse des tokens
Selon Hypura, le M4 Max est le sweet spot. Avec 64 Go, il peut faire tourner confortablement un modèle 70B (Q4), et générer à une vitesse double de celle du Pro
Ce projet fonctionne en pratique un peu comme une mémoire swap intelligente
Il est intéressant de voir qu’il régule l’usage du NVMe pour éviter de trop le solliciter
Mais si le NVMe est réellement très chargé, on peut craindre une réduction de durée de vie
Les SSD voient bien la durée de vie de leurs cellules diminuer selon le nombre d’écritures, mais il est extrêmement rare qu’une charge de lecture endommage le contrôleur
Si c’était le cas, cela indiquerait un autre problème dans le système
Il serait intéressant de comparer ce projet à des expériences précédentes, une autre tentative
Cette fois-ci, c’est basé sur mmap, et certains retours indiquaient un surcoût important