- Outil permettant d’exécuter l’inférence de modèles de diffusion en pur C/C++, y compris pour les familles SD, Flux et Wan, avec l’objectif d’une implémentation légère sans dépendances externes
- L’implémentation repose sur ggml et adopte une structure Plain C/C++ fonctionnant de la même manière que llama.cpp
- Les modèles pris en charge se répartissent entre modèles d’image, modèles d’édition d’image et modèles vidéo, avec notamment SD1.x, SD2.x, SDXL, SD3/SD3.5, FLUX, Qwen Image, Wan2.1/Wan2.2 et LTX-2.3
- Les fonctionnalités couvrent PhotoMaker, Control Net pour SD 1.5, les LoRA au format stable-diffusion-webui, LCM/LCM-LoRA, le décodage latent basé sur TAESD, l’upscale ESRGAN, les negative prompts et la prise en charge d’un tokenizer avec pondération des tokens
- Les backends d’exécution sont CPU, CUDA, Vulkan, Metal, OpenCL et SYCL, avec côté CPU la prise en charge d’AVX, AVX2 et AVX512 sur architecture x86
- Les plateformes prises en charge sont Linux, Mac OS, Windows et Android, ce dernier via Termux et Local Diffusion
- Les formats de poids pris en charge sont
.ckpt,.pth,.pt,.safetensorset.gguf, et le mode de conversion permet de convertir les poids des modèles en.ggufou.safetensors - Le flux d’utilisation de base consiste à récupérer un binaire précompilé depuis la page des releases ou à compiler depuis les sources, puis à télécharger les poids du modèle et lancer la génération d’image sous la forme
./bin/sd-cli -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat" - Des fonctions d’optimisation de l’usage mémoire sont proposées avec Flash Attention et le VAE tiling processing, tandis que l’optimisation du runtime, du placement des backends pour les paramètres et des performances fait l’objet de guides séparés
- Les options de reproductibilité sont séparées entre
--rng cudaet--rng cpu, avec pour objectif une cohérence respective avec le RNG GPU destable-diffusion-webuiet le RNG de ComfyUI - Dans les sorties PNG, les paramètres de génération sont insérés sous forme de chaîne de texte compatible webui
- Il existe des projets wrapper pour Golang, C#, Python, Rust et Flutter/Dart, et Jellybox, Local Diffusion, LocalAI, KoboldCpp, entre autres, utilisent
stable-diffusion.cppcomme backend de génération d’images - Le projet est en développement actif et l’API comme les options en ligne de commande peuvent changer fréquemment
1 commentaires
Avis sur Hacker News
Llama.cpp/ggml convient particulièrement bien aux LLM
Les besoins mémoire sont importants, la quantification est efficace, et la génération de tokens est étonnamment séquentielle et limitée par la bande passante mémoire, ce qui convient bien au CPU, et encore mieux au pipeline d’inférence CPU/GPU particulier de ggml
Mais Stable Diffusion, c’est différent. La quantification ne fonctionne pas aussi bien, UNet demande énormément de calcul, et la génération d’images par lots est efficace et utile même pour un seul utilisateur. C’est donc plus adapté aux GPU/GPU intégrés, et cela bénéficie beaucoup de la facilité de bidouille des implémentations Python
Pour Stable Diffusion, je pense que la bonne voie consiste à produire des exécutables via de la compilation machine learning. AITemplate est déjà très rapide https://github.com/VoltaML/voltaML-fast-stable-diffusion, et TVM Vulkan est aussi très prometteur si quelqu’un termine correctement une implémentation de démo https://github.com/mlc-ai/web-stable-diffusion
En plus, on conserve aussi l’essentiel de la bidouillabilité d’une implémentation PyTorch pure
Par exemple,
GGML_CUBLASest pris en charge à la compilation, et on obtient une accélération plutôt correcte par rapport au C/C++ purMême si cela prend un peu de temps, on peut l’exécuter sur un vieux portable
torch.compile, et je me souviens avoir travaillé dessus moi-mêmeJe vais voir si je peux retrouver les chiffres
C’est excellent d’avoir même implémenté CLIP
Ce serait sympa de l’extraire séparément et de le compiler en implémentation WebAssembly
Modification : il semble que quelqu’un ait déjà créé https://github.com/monatis/clip.cpp. Il ne reste plus qu’à en faire une version WebAssembly
C’est frustrant de se dire qu’il existe peut-être déjà quelque part, dans un coffre-fort secret, un modèle de niveau CLIP plus avancé
Modification : je ne parle pas de CLIP-2, mais d’une avancée d’un niveau d’importance comparable à CLIP
La configuration est incroyablement simple, au point que je l’ai essayé immédiatement pour la première fois
Je me demande quel niveau de performance est censé être normal
Sous Linux, je l’ai lancé sur un AMD Ryzen 7 5700G avec
cmake .. -DGGML_OPENBLAS=ON, sans GPU dédié, seulement le GPU intégréEn exécutant
./bin/sd -m ../models/sd-v1-4-ggml-model-f32.bin -p "a lovely cat", chaque étape d’échantillonnage a pris environ 12 secondes, et l’échantillonnage complet 246,40 secondesJe me demande si c’est la performance attendue
Modification : OpenBLAS n’était pas installé, donc ce flag n’a eu aucun effet
À l’époque, presque toutes les solutions exigeaient un tas de dépendances Python, et l’installation prenait tellement longtemps qu’elle finissait par échouer faute d’espace disque
Vraiment, littéralement, cela remplace plusieurs gigaoctets d’espace disque par un seul binaire de 799 Ko. En prime, si l’on utilise le format Q8_0, qui semble être le plus rapide, on économise aussi environ 2,3 Go de données
Cela dit, il semble y avoir des bugs avec les tailles d’image autres que la taille par défaut 512x512. Certaines tailles comme 544x544 ont tendance à provoquer un échec d’assertion, les tailles inférieures à 512x512 produisent parfois des images incohérentes, et les tailles inférieures à 384x384 le font presque toujours
[0] https://news.ycombinator.com/item?id=32555608
Les implémentations C/C++ liées à l’IA ont quelque chose de particulièrement séduisant
Le code paraît propre et intuitif, et cela donne l’impression que tout le domaine de l’IA devient tangible et apprenable
Est-ce parce que l’écosystème Python est trop brouillon ?
La version Python utilise aussi du code C et C++ pour la vitesse, mais ici tout est dans un seul langage
Les trois facteurs qui permettent d’obtenir du code propre agissent donc ensemble
C’est agréable de voir des gens du machine learning sortir de Python et utiliser un langage qui exploite le matériel de façon optimale, sans devoir configurer un environnement spécial pour compiler et exécuter
D’abord, le projet de l’article n’utilise pas le GPU, comme llama.cpp, alors que la plupart du code Python de machine learning utilise le GPU. Écrire du code Python qui exploite le GPU de façon optimale n’est pas difficile. On pourrait dire que le GPU est un environnement spécial pour compiler et exécuter, mais on peut considérer qu’il est bien mieux adapté à ce problème
Ensuite, comme llama.cpp, le projet de l’article a produit du code efficace et très spécialisé après qu’il a été confirmé que des modèles précis comme Stable Diffusion/LLaMA fonctionnaient bien. En revanche, là où Python brille, c’est à l’étape de prototypage, quand on n’a pas encore trouvé le bon modèle. Je n’ai pas encore vu de prototypage aussi simple et confortable en C++
Il ne s’agit pas de dénigrer l’excellent travail que font les gens de llama.cpp dans le domaine du machine learning sur CPU. Simplement, les problèmes résolus sont complètement différents
En interne, tout est depuis longtemps en CUDA, C et C++
Python n’est qu’une colle très efficace qui assemble tout cela
C’est la seule façon dont j’ai pu exécuter ces modèles sans problèmes pénibles. La différence est énorme. Le duo CUDA et Linux n’est déjà pas idéal, et AMD avec Windows est catastrophique. Je ne pense probablement pas être le seul
Au final, est-ce que tout n’était qu’une question de bande passante mémoire ?
L’architecture GPU ne se résume pas à la puissance de calcul : elle place aussi la mémoire de travail près des unités de calcul. Chaque unité dispose d’une mémoire locale synchronisée avec la mémoire globale. Est-ce une grande partie de la raison pour laquelle les GPU sont forts sur ce type de tâche ?
Ça ressemble à du C++, alors pourquoi parler de C/C++ ?
J’ai vu ce dépôt aujourd’hui, je l’ai récupéré, puis j’ai compilé une
.dylibsur Mac et généré des bindings depuis le fichier d’en-tête fourni avec l’outil ffi-gen de DartJe fais des essais avec Flutter et j’utilise FFI pour éviter de lancer un sous-processus
Résultat : un gros mal de tête et une app cassée. Je réessaierai demain avec les idées claires
Cela dit, le dépôt lui-même est excellent, et sur M1 il s’exécute en f16 en moins de 10 minutes
Les exemples de différents niveaux de quantification sont assez impressionnants
Le passage de f16 à q8_0 ressemble davantage à un changement de direction qu’à une perte de qualité. Le résultat en q5_1 semble difficile à distinguer de q8_0
On perd le déterminisme des modèles haute précision, mais en pratique cela pourrait être tout à fait exploitable
Y a-t-il des benchmarks ?
https://github.com/leejet/stable-diffusion.cpp/issues/1
cmake .. -DGGML_CUBLAS=ON -DCMAKE_CUDA_COMPILER=/opt/cuda/bin/nvccet utilisé une NVIDIA GeForce RTX 2060 SUPERLe modèle a été converti en FP16
Avec cette option, le temps par itération est compris entre 8,5 et 9 secondes, et le temps total pour générer une image est d’environ 200 secondes