mllm: eine C++-Inferenz-Engine für multimodale Modelle auf Telefonen und Edge-Geräten
Fast Multimodal LLM on Mobile Devices
Auf einen Blick
- Was ist das?
- mllm bündelt Konvertierung, Laufzeit und Quantisierung für Qwen3, Qwen3-VL und MiniCPM5 auf Arm-CPU, OpenCL, QNN-NPU und Ascend. Der interessante Teil ist nicht die Modellliste, sondern die Entscheidung, die Android-Anbindung als In-App-Server in Go zu bauen.
- Für wen ist es gedacht?
- Wer Qwen3 oder Qwen3-VL auf einem Telefon oder einem Jetson-Board lokal betreiben will und bereit ist, sich mit mllm-convertor, dem mllm-eigenen Modellformat und den Backend-Schaltern auseinanderzusetzen, findet hier einen zusammenhängenden Pfad von PyTorch-Checkpoint bis Laufzeit. Wer ein reines Textmodell auf einem Server mit CUDA betreibt, ist mit llama.cpp oder vLLM besser bedient, weil dort weder die Vision-Pipeline noch die NPU-Kompilierung mitschwingt.
- Darf ich es kommerziell nutzen?
- Ja. MIT ist eine freizügige Lizenz: Sie dürfen darauf aufbauende Software nutzen, verändern und verkaufen, solange Sie die Urheberrechts- und Lizenzhinweise beibehalten.
- Wird es noch gepflegt?
- Ja. Die letzten Commits kamen vor 7 Tagen.
- In welcher Sprache ist es geschrieben?
- Hauptsächlich C++, laut der Sprachstatistik von GitHub.
Die Antworten beruhen auf den GitHub-Daten des Projekts (zuletzt abgeglichen am 15. September 2026) und auf unserer Analyse. Sie sind keine Rechtsberatung.
TIEFGEHENDE OPEN-SOURCE-ANALYSE
Welches Problem mllm löst und für wen es gedacht ist
Ein multimodales Modell auf einem Telefon zu betreiben ist kein reines Rechenproblem. Es ist ein Problem der Kette: Ein PyTorch- oder SafeTensors-Checkpoint muss in ein Format gebracht werden, das eine C++-Laufzeit laden kann, die Gewichte müssen quantisiert werden, damit sie in den Speicher passen, und die Vision-Kodierung muss auf derselben Hardware laufen wie der Text-Decoder. Genau diese Kette beschreibt das Repository als Aufgabe von mllm. Der mllm-convertor nimmt Checkpoints aus der PyTorch-Welt entgegen, quantisiert und konvertiert sie in das mllm-Format, das die Laufzeit anschließend lädt. Die Zielgruppe sind damit nicht Anwender, die eine fertige App suchen, sondern Entwickler, die ein konkretes Modell auf einer konkreten Geräteklasse zum Laufen bringen wollen. Die unterstützten Ziele sind Arm-CPU, OpenCL-GPU, Hexagon-NPU über QNN und Ascend-NPU. Das ist eine ungewöhnlich breite Aufstellung für ein Projekt dieser Größe, und sie ist zugleich der Grund, warum die Dokumentation nach Backend zerfällt statt einen einzigen Weg zu beschreiben.
Der In-App-Server: Go statt klassischer JNI-Anbindung
Die auffälligste Designentscheidung steht im Abschnitt zur Android-Demo. Statt die Inferenz direkt über JNI in die App zu hängen, beschreibt das Repository eine Client-Server-Architektur, die vollständig auf dem Gerät bleibt. Der Serverteil ist in Golang geschrieben und wird als mllm_server.aar ausgeliefert. Die Begründung im Text lautet, dass diese Schicht die UI von der schweren Inferenzberechnung entkoppelt. Das ist ein echter Architekturunterschied und nicht nur eine Umbenennung: JNI-Aufrufe blockieren den aufrufenden Thread oder verlangen eine sorgfältige Threadverwaltung, während ein lokaler Server die Inferenz als eigenen Prozess oder Dienst kapselt und die UI nur noch über eine lokale Schnittstelle mit ihm spricht. Der Preis dafür ist ein zusätzlicher Kopierschritt für Ein- und Ausgaben sowie ein zweiter Sprach-Stack im Build. Wer nur eine einzelne Funktion ausführen will, zahlt diese Kosten ohne Gegenwert. Für Streaming-Ausgaben, wie sie die Demo für Qwen3 und DeepSeek-OCR auf Android zeigt, ist die Trennung dagegen naheliegend.
Quantisierungspfade und was sie auf welcher Hardware bedeuten
mllm v2 führt mehrere Quantisierungsschemata nebeneinander. Für die CPU listet die Modelltabelle Einträge wie w4a8 mit i8mm-kai, also 4-Bit-Gewichte mit 8-Bit-Aktivierungen und Arm-Kernel-Pfaden. Für Hexagon-NPU erscheint Qwen3-1.7B als W4A16-SM8650 mit der Kennzeichnung Qnn-AOT, also Ahead-of-Time-Kompilierung des Graphen. Für Ascend gibt es W8A8-Varianten. Die Jetson-Seite trennt zusätzlich nach Backend: Der W4A16-Pfad nutzt AWQ compressed tensors und Marlin GEMM, der W8A8-Pfad Triton-Quantisierung pro Token für die Aktivierungen und CUTLASS-INT8-GEMM. Das sind keine austauschbaren Schalter. Jeder Pfad bindet an eine bestimmte Kernel-Bibliothek und damit an eine bestimmte Hardware- und Toolchain-Kombination. Wer W8A8 auf Jetson wählt, bekommt Triton und CUTLASS mit; wer W4A16 wählt, bekommt Marlin. Ein Wechsel des Schemas ist deshalb ein Wechsel der Abhängigkeiten, nicht nur ein anderer Wert in einer Konfigurationsdatei.
Die Zahlen aus dem Repository und ihre Grenzen
Das Repository nennt für pymllm auf Jetson Orin einen Prefill-Vorsprung gegenüber llama.cpp. Für Qwen3-VL-2B in W8A8 auf einem AGX Orin 32GB werden bis zu 3.12x Prefill-Beschleunigung und etwa 12243 tok/s Prefill-Durchsatz angegeben, gemessen bei input_len=2048 und output_len=128. Die Decode-Rate wird ausdrücklich als ungefähr gleichauf mit llama.cpp beschrieben, mit kleinen Gewinnen oder Verlusten je nach Modell, Gerät und Quantisierung. Diese Einschränkung ist wichtiger als die Prefill-Zahl, denn sie sagt: Der Vorteil liegt in der Verarbeitung des Prompts, nicht in der Generierung. Die Modelltabelle nennt für multimodalen Prefill Mittelwerte über wiederholte Läufe, etwa 6443.59 TPS für Qwen3-VL-2B in W8A8 auf AGX Orin 32GB gegenüber 4875.75 in FP16. Der Text weist darauf hin, dass bench_one_batch --image den vollständigen Pfad aus Vision-Encoding und Bild- plus Text-Token-Prefill misst. Wer diese Zahlen mit eigenen Messungen vergleichen will, muss denselben Pfad messen, sonst vergleicht er Vision-Encoding gegen reines Text-Prefill.
Inbetriebnahme: Konverter, Laufzeit, CLI
Der Weg in den Betrieb führt über den mllm-convertor. Das Repository beschreibt ihn als Einstiegspunkt, der PyTorch- und SafeTensors-Modelle direkt aufnimmt, quantisiert und in das mllm-Format überführt, das die Laufzeit lädt. Für die meisten Modelle ist dieser Schritt bereits erledigt: Die Modelltabelle verlinkt fertig konvertierte Checkpoints auf ModelScope, etwa mllmTeam/Qwen3-1.7B-w4a8-i8mm-kai für die CPU oder mllmTeam/Qwen3-1.7B-Qnn-AOT-SM8650 für Hexagon. Für die QNN-AOT-Ausführung verweist das Repository auf eine eigene Quick-Start-Seite unter qnn_backend/aot_execute.html, für MiniCPM5-2B auf examples/minicpm5/README.md. Auf der Jetson-Seite ist bench_one_batch das genannte Werkzeug, mit dem Schalter --image für den multimodalen Pfad. Wer die Vorab-Checkpoints nutzt, überspringt den Konverter und beginnt direkt bei der Laufzeit. Wer ein eigenes Modell einbringen will, beginnt beim Konverter und muss dessen Ausgabeformat verstehen, bevor die Laufzeit überhaupt ins Spiel kommt. Das Repository beschreibt diesen Ablauf grafisch, ohne die Formatdetails im README auszubreiten.
Wo mllm die falsche Wahl ist
Die klarste Grenze zieht das Projekt selbst: MLLM V1 wird abgekündigt. Der Eintrag vom 28. August 2025 kündigt an, dass der Support für V1 bald endet, dass V1 vorher noch GPT-OSS aufnimmt und dass das Projekt danach auf V2 umstellt. V2 liegt auf einem eigenen Branch und bringt laut Ankündigung einen pythonischeren Modellbau mit Eager Execution, Kompilierungsunterstützung für NPU-Integration, parallele Ausführung mehrerer Modelle und eine überarbeitete Engineering-Grundlage. Wer heute auf V1 aufsetzt, baut auf einem Stand, für den das Projekt selbst ein Enddatum nennt. Die zweite Grenze ist die Hardwarebindung. Die Quantisierungspfade sind an konkrete Chips gebunden, SM8650 für den genannten QNN-Checkpoint, AGX Orin und Orin NX für die Jetson-Zahlen. Ein Modell, das auf einem dieser Ziele konvertiert wurde, ist damit nicht automatisch auf einem anderen lauffähig. Und wer ausschließlich Text auf einem Server mit einer einzelnen CUDA-GPU verarbeitet, für den ist der gesamte Vision- und NPU-Teil der Engine unnötiger Ballast.
llama.cpp als Gegenentwurf
Der naheliegende Vergleich ist llama.cpp, und das Repository führt ihn selbst, weil es Decode-Raten gegen llama.cpp stellt. Der Unterschied liegt im Ansatz. llama.cpp ist auf GGUF als einheitliches Format und auf eine breite Palette von Quantisierungsstufen für Textmodelle ausgerichtet; die Portabilität kommt aus einem einzigen Format, das auf vielen Backends lädt. mllm geht den umgekehrten Weg: Pro Zielhardware existiert ein eigener Konvertierungspfad mit eigener Quantisierung und eigenen Kernel-Bibliotheken, dafür reicht die Kette bis in die multimodale Verarbeitung und bis auf NPUs, für die llama.cpp keinen QNN-AOT-Graphen kompiliert. Der praktische Unterschied zeigt sich bei der Frage, was man mitbringt. Ein GGUF-Modell läuft in llama.cpp ohne weiteren Konvertierungsschritt; ein Qwen3-VL-Checkpoint muss für mllm erst durch den Konverter und auf das Ziel zugeschnitten werden. Dafür bekommt man den Vision-Pfad und die NPU-Ausführung, die llama.cpp in dieser Form nicht abdeckt.
Lizenz, Wartung und der Aufwand beim Umstieg auf V2
mllm steht unter der MIT-Lizenz. Das ist eine permissive Lizenz: Sie erlaubt Nutzung, Änderung und Weitergabe, auch in proprietären Produkten, und verlangt im Wesentlichen die Beibehaltung des Lizenz- und Urheberrechtshinweises. Das Repository enthält keine Hinweise auf zusätzliche Bedingungen, und ich gebe hier keine Rechtsberatung; wer mllm in ein kommerzielles Produkt einbettet, sollte die Lizenztexte der mitgelieferten Bestandteile prüfen, denn die Quantisierungspfade ziehen Abhängigkeiten wie CUTLASS, Triton und Marlin nach sich, die eigene Lizenzen haben. Der Wartungsaufwand liegt weniger im Code als in der Versionsumstellung. Zwischen 1.0.0 im Januar 2024 und 2.0.0 im Februar 2026 liegen gut zwei Jahre, und die Ankündigung vom August 2025 beschreibt V2 als Neuentwicklung mit anderem Modellbau und Kompilierungsunterstützung. Wer V1-Code hat, muss den Umstieg einplanen, nicht nur ein Upgrade. Auf der Habenseite steht eine regelmäßige Release-Kadenz: Die Einträge reichen von Juli 2025 bis September 2026 und decken MiniCPM5, Ascend, QNN-AOT und die Jetson-CUDA-Unterstützung ab. Die CUDA-Unterstützung auf Jetson Orin und Thor ist im Repository ausdrücklich als experimentell und in aktiver Entwicklung markiert. Wer darauf baut, sollte das als bewegliches Ziel behandeln.
Redaktionelles Fazit
Wer Qwen3 oder Qwen3-VL auf einem Telefon oder einem Jetson-Board lokal betreiben will und bereit ist, sich mit mllm-convertor, dem mllm-eigenen Modellformat und den Backend-Schaltern auseinanderzusetzen, findet hier einen zusammenhängenden Pfad von PyTorch-Checkpoint bis Laufzeit. Wer ein reines Textmodell auf einem Server mit CUDA betreibt, ist mit llama.cpp oder vLLM besser bedient, weil dort weder die Vision-Pipeline noch die NPU-Kompilierung mitschwingt. Vor dem ersten Einsatz würde ich prüfen, ob für das konkrete Zielmodell ein fertig konvertierter Checkpoint auf ModelScope oder Hugging Face existiert, denn ohne diesen beginnt die Arbeit beim Konverter und nicht bei der Inferenz.
Community-Notizen