Apple Silicon en macOS VM's: 11–16× snellere LLM-inferentie met llama.cpp

Als je Cua vanaf het begin volgt, herinner je je wellicht dat het begon met een Show HN-lancering voor Lume, onze macOS-virtualisatiestack.

Vandaag delen we het eerste resultaat van een breder effort om die Virtualization.framework-fundering te verbinden met de lokale computer-use omgevingen achter Cua Driver en de infrastructuur achter Cua Cloud en Fleets: een kleine, op procesniveau beperkte compatibiliteitslaag die nieuwere Metal 'fast paths' ontgrendelt binnen een macOS-gast (guest).

We brengen dit werk vandaag uit als een research-release onder dezelfde permissieve licentie als Lume en Cua, zodat anderen de resultaten kunnen reproduceren en kunnen helpen in kaart te brengen welke Apple Silicon-chips, macOS-versies en Metal-workloads hiervan profiteren.

Gebruikers van Apple Vz zijn elders ook tegen deze beperkingen aangelopen. Tart, een andere opvallende CLI gebouwd op het Virtualization.framework van Apple, heeft een openstaande issue genaamd “No GPU passthrough in macOS guest?”, waarin wordt gevraagd of het framework bruikbare graphics en fatsoenlijke LLM-prestaties kan bieden in een macOS VM-gast. De VM blijft namelijk de virtuele GPU gebruiken die Apple levert. Ons werk stelt nieuwere Metal-paden bloot op dat apparaat en dicht zo een deel van het praktische gat.

Op een M1 Ultra verwerkte TinyLlama 1.1B, draaiend via llama.cpp, prompts 11,08× sneller en genereerde tokens 16,36× sneller dan dezelfde workload in een standaard VM. De promptverwerking bereikte 98% van ons bare-metal resultaat. De broncode, build-scripts, capability probe en ruwe benchmark-logs zijn bijgevoegd, zodat u het resultaat kunt inspecteren en reproduceren.

We hebben het experiment herhaald met Google's Gemma 4 12B QAT Q4_0, een model van 6,98 GB dat dit jaar is uitgebracht. Dezelfde laag verbeterde de promptverwerking met 7,20× en de tokengeneratie met 14,54×. De ontgrendelde VM bereikte 99,59% van de bare-metal promptsnelheid en 94,82% van de bare-metal generatiesnelheid.

Het knelpunt binnen een macOS VM

Apple's Virtualization.framework presenteert aan een macOS-gast een virtueel grafisch apparaat. De gast stuurt Metal-werk via een speciaal gebouwd GPU-stuurprogramma, en de host-stack van Apple voert dit uit op de fysieke GPU. Deze constructie is paravirtualisatie, waarbij de host de controle over de hardware behoudt en de gast een virtualisatie-bewust apparaat gebruikt.

Dit verschilt van andere virtualisatiestacks gebouwd op QEMU en KVM, die een andere architectuur kunnen gebruiken. Op x86 Linux-hosts kan VFIO een compatibel fysiek PCI-apparaat of hardwarefunctie toewijzen aan een VM via een IOMMU, waardoor de gast direct toegang krijgt tot dat apparaat. Dit is het model dat gewoonlijk wordt bedoeld met 'GPU passthrough'.

In onze standaard Tahoe VM rapporteerde het paravirtualiseerde apparaat ongeveer een Apple 5-serie familie, 32 KB aan maximaal threadgroup-geheugen, en was SIMD-group matrix-ondersteuning niet beschikbaar. Moderne Metal-software gebruikt deze antwoorden om kernels te selecteren; hierdoor koos llama.cpp voor een trager pad, ook al kon het apparaat nieuwere kernels uitvoeren.

Apple documenteert GPU-mogelijkheden via GPU-families en feature-tabellen en adviseert om het apparaat tijdens runtime te bevragen. Dat maakt de gerapporteerde capaciteitsgrens cruciaal: applicaties doen precies wat het platform hen vertelt.

De oplossing: een Metal capability shim op procesniveau

We hebben een kleine Metal capability shim gebouwd (een compatibiliteitslaag tussen een applicatie en een API) die draait binnen één gastproces. Deze onderschept geselecteerde Metal-capaciteitsvragen en wijzigt de antwoorden die aan dat proces worden teruggestuurd. Metaal-applicaties gebruiken deze antwoorden om kernels te selecteren; door de geteste Apple-familie en threadgroup-geheugenwaarden terug te geven, kan llama.cpp zijn nieuwere GPU-paden kiezen.

Voor ons geteste profiel doet de shim het volgende:

  • Geeft antwoord op supportsFamily: tot en met Apple familie 9 (1009).
  • Verhoogt het gerapporteerde maximale threadgroup-geheugen van 32 KB naar 64 KB.

Dit was voldoende voor de geteste llama.cpp-build om nieuwere SIMD-group reduction, SIMD-group matrix en bfloat16 paden te selecteren:

CapabilityStandaard gastGetest profiel
supportsFamily:1009falsetrue
SIMD-group matrixuitaan
SIMD-group reductionuitaan
bfloat16uitaan
Maximaal threadgroup-geheugen32 KB64 KB

Het geteste profiel wijzigt slechts twee gerapporteerde waarden: de Apple-familie antwoorden en de limiet van het threadgroup-geheugen. Common, Mac, Metal en working-set-size waarden behouden hun standaardinstellingen tijdens de benchmark. We hebben de oorspronkelijke private feature-profile hook, clock and timing interposition, mesh substitution, ray-tracing override, argument-layout guard en pipeline-compilation fallback uit de research-hook verwijderd. De broncode is klein genoeg om te auditen, en een foutieve of ontbrekende configuratie houdt het proces op zijn standaard capaciteitspad.

De workload blijft op het grafische pad van Apple's Virtualization.framework en wordt uitgevoerd op de Apple GPU van de host. De wijzigingen in capaciteit zijn beperkt tot het geïnjecteerde gastproces.

Fysieke GPU-toewijzing, ruwe PCI- of VFIO-passthrough en kernelwijzigingen vallen buiten dit mechanisme. Een gerapporteerde familie beschrijft de paden die door onze tests zijn gedekt; elk aanvullend Metal API vereist een aparte validatie. De shim ontgrendelt Metal-mogelijkheden op het bestaande virtuele GPU-pad van Apple. VM-gebruikers komen deze bredere beperking vaak tegen onder de naam “GPU passthrough”.

Nieuwe resultaten van het minimale artefact

We hebben getest op één Apple M1 Ultra met een 48-core GPU en macOS 26.6.1. De gast was de huidige publieke Tahoe Cua-image (macOS 26.5.2, 8 vCPU en 16 GiB) draaiend in Lume 0.5.1. Alle drie de runs gebruikten de officiële llama.cpp b10167 release en hetzelfde TinyLlama 1.1B Chat Q4KM model.

Het gebruikte commando was:

llama-bench -m tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf \
-p 512 -n 128 -r 10 -t 8 -ngl -1 -o json

De onderstaande waarden zijn medianen van de tien monsters die per benchmarkrij zijn gegenereerd:

WorkloadBare-metal hostStandaard gastOntgrendelde gastVersnelling gastOntgrendeld / host
Promptverwerking, 512 tokens4.871,99 tok/s431,86 tok/s4.786,70 tok/s11,08×98,25%
Tokengeneratie, 128 tokens286,71 tok/s12,63 tok/s206,60 tok/s16,36×72,06%

De promptverwerking bereikte bijna het resultaat van de host. De generatie bereikte 72,06% van de hostsnelheid, waardoor er een meetbaar VM-gat overblijft. De winst hangt af van de host GPU, de gastversie, de applicatie en de vorm van de workload.

De ruwe TinyLlama resultaten en het omgevingsrecord bevatten de exacte image digest, model- en binary hashes, commando's, JSON-output, stderr en checksums. Deze release-candidate resultaten certificeren de gereduceerde shim die in dit bericht wordt gebruikt.

Een actueel 12B model

TinyLlama is een nuttige gecontroleerde benchmark omdat het snel draait en het Metal-pad duidelijk blootlegt. We wilden ook een groter model testen dat ontwikkelaars vandaag de dag zouden kunnen kiezen, dus we hebben Google's officiële Gemma 4 12B instruction-tuned QAT Q4_0 GGUF door dezelfde llama.cpp binary gehaald.

De host, VM, shim, benchmarkvorm en de methode met tien monsters bleven gelijk. We hebben speculatieve decoding uitgeschakeld en de multimodal projector niet geladen om de vergelijking op hetzelfde Metal-inferentiepad te houden:

WorkloadBare-metal hostStandaard gastOntgrendelde gastVersnelling gastOntgrendeld / host
Promptverwerking, 512 tokens517,88 tok/s71,66 tok/s515,76 tok/s7,20×99,59%
Tokengeneratie, 128 tokens52,38 tok/s3,41 tok/s49,67 tok/s14,54×94,82%

Het bewijs voor Gemma 4 koppelt de modelrevisie van Google en de SHA-256 aan de uiteindelijke ruwe monsters. We hebben een voorlopige standaardserie weggegooid en opnieuw uitgevoerd nadat we een andere host compute workload detecteerden. De behouden bestanden voor de standaard, ontgrendelde en bare-metal runs komen uit hetzelfde venster zonder concurrentie en tonen nauwe monsterbereiken.

We hebben ook MLX-LM 0.31.3 getest met mlx-community/Llama-3.2-3B-Instruct-4bit op MLX 0.32.0. De prestaties bleven vlak omdat MLX-LM in de standaard VM al snel was:

WorkloadStandaard gastOntgrendelde gastRatio
Promptverwerking, 512 tokens1.656,55 tok/s1.665,47 tok/s1,005×
Tokengeneratie, 128 tokens172,09 tok/s170,86 tok/s0,993×

Dit vlakke resultaat hielp bij het definiëren van het releaseprofiel. Tijdens de ablatie zorgde het adverteren van MTLGPUFamilyMetal3 ervoor dat MLX een residency-set aanvraagde die niet beschikbaar was via het paravirtualiseerde apparaat. De release shim beperkt gewijzigde antwoorden tot Apple-familie enums en houdt Metal 3 op de standaardwaarde. De relevante MLX-branch is zichtbaar in de Metal residency-implementatie.

Positie binnen het platform van Apple

Dit draait volledig op Apple-hardware via het paravirtualiseerde GPU-pad dat Apple levert met Virtualization.framework. De shim beïnvloedt geselecteerde waarden die door één gastproces worden gelezen. De host, de gastkernel, andere gastprocessen, de status van contentbescherming en de licentiestatus behouden hun bestaande configuratie.

De techniek vertrouwt op private, versiegevoelige behavior in de Metal-implementatie van de gast. Apple kan dit wijzigen tussen macOS-releases, dus we testen elke host- en gastcombinatie onafhankelijk. Niet-ondersteunde methoden houden het proces op zijn standaardpad, en elk aanvullend API vereist een eigen virtualisatietest.

We zouden graag verduidelijking ontvangen van Apple over het beoogde gedrag en de ondersteunbaarheid van het onbeperkte feature-niveau voor paravirtualiseerde graphics. Apple-engineers die werken aan Metal of Virtualization.framework kunnen ons bereiken via vz@trycua.com.

Zelf proberen in een Lume VM

De broncode bevindt zich in libs/lume/metal-capability-shim. Build en verifieer beide architectuur-specifieke dylibs:

cd libs/lume/metal-capability-shim
./Scripts/build.sh
./Scripts/verify.sh

Stop de VM, schakel het onbeperkte feature-niveau in voor VM's die zijn gestart door uw macOS-gebruiker, en start deze opnieuw op:

lume stop my-vm
defaults write com.apple.gpusw.ParavirtualizedGraphics \
ForceUnrestrictedDeviceFeatureLevel -bool true
lume run my-vm

Kopieer de bijbehorende dylib en de probe of workload naar de gast, en beperk de activatie tot dat proces:

lume ssh my-vm \
"DYLD_INSERT_LIBRARIES=/path/to/LumeMetalCapabilities-arm64.dylib \
LUME_METAL_APPLE_FAMILY_MAX=1009 \
/path/to/metal-capabilities 1009"

Voor een langdurige inferentie-server, renderer of worker kunt u een per-workload LaunchAgent gebruiken. Stel DYLDINSERTLIBRARIES in de omgeving van die workload in, zodat de login-sessie standaard blijft. De Lume-gids bevat een volledig sjabloon, checksums, verificatiestappen en instructies voor rollback.

Door de omgevingsvariabelen te verwijderen en de workload te herstarten, keert deze terug naar het standaardgedrag. Om de hostvoorkeur te herstellen: stop de VM, verwijder ForceUnrestrictedDeviceFeatureLevel en start de VM opnieuw op.

Beperkingen

  • Experimenteel en versiegevoelig: De shim gebruikt private details van de Metal-implementatie in de gast die in elke macOS-release kunnen veranderen.
  • Per proces: Het beïnvloedt alleen de geïnjecteerde workload en diens kinderen; geharde of platform-beschermde executables kunnen library-injectie weigeren.
  • Geconfigureerd capaciteitsprofiel: Het rapporteert de Apple-familie waarden die door onze tests zijn gedekt. Fysieke GPU-capaciteitsontdekking valt buiten dit bereik.
  • Beperkte validatie: De huidige bewijzen beslaan de capability probe, twee llama.cpp workloads en één MLX-LM compatibiliteitsrun op de genoemde M1 Ultra host en Tahoe gast. Aanvullende chips, gastreleases, modellen en Metal API's vereisen aparte tests.
  • Nog steeds een VM: Bestaande rendering- en virtualisatielimieten van Virtualization.framework blijven van kracht.

Afsluiting

De conservatieve antwoorden van de gast verhulden een verrassend capabel GPU-pad. Op onze testmachine zorgden twee nauw gedefinieerde wijzigingen in de capaciteit ervoor dat TinyLlama promptverwerking steeg van 432 naar 4.787 tokens per seconde. Met Gemma 4 12B steeg de promptverwerking van 71,66 naar 515,76 tokens per seconde en de generatie van 3,41 naar 49,67, terwijl de workload op de bestaande GPU-brug van Apple bleef.

Lume begon als een manier om macOS VM's praktisch te maken voor ontwikkelaars. Dit resultaat geeft ons een basis om tests uit te voeren over meer Apple Silicon-generaties, gastreleases en Metal workloads.

Wilt u helpen? Geef Cua een ster op GitHub en test de shim in uw setup. Open een issue met uw hostchip, host- en gastversies, exacte workload en zowel de standaard als de ontgrendelde resultaten. Als u een nieuwe combinatie valideert of de shim verbetert, stuur dan een pull request.