La fusione CUDA di llama.cpp arriva con un'elaborazione dei prompt fino al 10,7% più veloce
llama.cpp ha integrato un'ottimizzazione CUDA il 25 ottobre, con benchmark del contributore che riportano guadagni nell'elaborazione dei prompt fino al 10,7% su DGX Spark. La modifica di fusione CUDA di llama.cpp combina operazioni all'interno della rete feed-forward di un modello, il componente che trasforma le rappresentazioni dei token tra le fasi di attenzione. Il suo vantaggio riguarda la lettura dei prompt, non la produzione di ogni token successivo della risposta.
Le misurazioni del contributore anujj mostrano miglioramenti anche su RTX 5090 di NVIDIA, sebbene le percentuali di guadagno riportate siano inferiori. Si tratta di confronti riportati dall'autore, non di risultati riprodotti in modo indipendente. La modifica sottostante è verificabile nel codice integrato, mentre il suo valore pratico dipende ancora dal formato del modello, dall'hardware e dal carico di lavoro.
Il confronto centrale è tra l'esecuzione fusa, che combina operazioni correlate, e un percorso di esecuzione esistente che pianifica tali operazioni separatamente. Una proposta precedente perseguiva la stessa opportunità in modo diverso. L'approccio integrato riorganizza il modo in cui i valori raggiungono i thread GPU, riducendo il lavoro intermedio senza modificare i pesi memorizzati del modello. Per gli sviluppatori che eseguono localmente prompt lunghi, si tratta quindi di uno specifico miglioramento software che vale la pena misurare rispetto a un'implementazione esistente.
Cosa cambia con l'integrazione della fusione CUDA di llama.cpp
La modifica integrata estende un'ottimizzazione esistente a carichi di elaborazione dei prompt più ampi su hardware NVIDIA supportato.
CUDA, la piattaforma di calcolo GPU di NVIDIA, fornisce l'ambiente di esecuzione per questa implementazione. MMQ, il percorso di moltiplicazione matriciale quantizzata di llama.cpp, gestisce i calcoli che coinvolgono pesi di modello compressi. La quantizzazione rappresenta tali pesi con una precisione numerica ridotta per diminuire i requisiti di archiviazione e memoria.
La modifica di fusione integrata prende di mira una sequenza ricorrente all'interno dei layer feed-forward densi supportati. Le proiezioni gate e up separate trasformano lo stesso input. Un'unità lineare con gate, o GLU, combina quindi i rispettivi risultati attraverso un'attivazione e una moltiplicazione elemento per elemento.
In precedenza, i carichi di lavoro più grandi su questo percorso gestivano le proiezioni e la loro combinazione come operazioni separate. Secondo anujj, la nuova implementazione esegue tale sequenza tramite un unico kernel di moltiplicazione matriciale. Un kernel è una funzione lanciata attraverso i thread GPU.
Questo consolidamento è importante perché anche il lavoro circostante consuma risorse. Preparare gli input, avviare le operazioni, scrivere risultati intermedi e leggerli nuovamente possono tutti aggiungere overhead. Combinare le operazioni crea l'opportunità di eliminare parte di quel traffico, mantenendo al contempo la trasformazione matematica prevista.
La modifica non introduce questa idea simultaneamente in ogni percorso di esecuzione. Il contributore afferma che la fusione per batch piccoli esisteva già tramite altre implementazioni matrice-vettore. I calcoli matrice-vettore servono in genere carichi di lavoro più piccoli rispetto ai calcoli matrice-matrice qui messi in evidenza.
Questa distinzione spiega perché l'annuncio si concentra sull'elaborazione dei prompt. Il prefill, la fase che elabora un prompt in ingresso prima della generazione della risposta, può esporre batch di lavoro più grandi. Il percorso di esecuzione ora supportato affronta quella parte dell'inferenza.
Anche la cronologia è rilevante. La descrizione di apertura della pull request risale al 4 ottobre, mentre il progetto l'ha integrata il 25 ottobre. L'integrazione porta l'implementazione nel ramo master del progetto. Non stabilisce che ogni applicazione distribuita includa già la modifica.
Un'applicazione desktop, un container o un progetto downstream può incorporare llama.cpp secondo la propria tempistica. Gli utenti devono quindi identificare la versione effettiva del runtime dietro un'interfaccia. Un aggiornamento dell'applicazione e un'integrazione del codice sorgente upstream sono eventi distinti.
Per i file di modello esistenti, la compatibilità è relativamente semplice. Il contributore afferma che il layout dei pesi memorizzati rimane invariato. Gli utenti non dovrebbero interpretare il riferimento del titolo a righe interlacciate come un requisito per scaricare file di modello appositamente riorganizzati.
La notizia immediata è quindi circoscritta e concreta. Una sequenza di esecuzione supportata dispone ora di un'ulteriore ottimizzazione disponibile upstream. Che una specifica richiesta la utilizzi dipende dai controlli di idoneità del runtime.
I guadagni dei benchmark favoriscono Spark, con un incremento minore su RTX 5090
I risultati riportati mostrano una direzione coerente nelle configurazioni elencate, ma la loro entità varia in base all'hardware e alla quantizzazione.
L'autore ha testato DGX Spark utilizzando il suo processore GB10 e ha testato separatamente una RTX 5090. Entrambe le tabelle dei benchmark confrontano una baseline etichettata master con la pull request. Ogni riga elencata riporta una maggiore velocità di elaborazione dei prompt per l'implementazione modificata.
Su DGX Spark, il maggiore miglioramento percentuale elencato riguarda Llama-3.1-8B con IQ4_XS, un formato di pesi compresso. Il throughput aumenta da 2.933,9 a 3.249,0 token al secondo. L'autore riporta un guadagno del 10,7% per tale configurazione.
Altri risultati di Llama-3.1-8B su Spark restano al di sotto di questo picco, pur mantenendo miglioramenti riportati sostanziali. La configurazione Q4_K_M passa da 2.749,3 a 3.000,6 token al secondo. La configurazione Q5_K_M aumenta da 2.699,4 a 2.960,3 token al secondo.
Tali etichette identificano diverse configurazioni di modello quantizzato, anziché misurazioni intercambiabili dello stesso binario. I loro risultati mostrano perché gli utenti dovrebbero confrontare il formato esatto del file quando valutano un'ottimizzazione. Il solo nome di una famiglia di modelli omette una variabile rilevante.
La tabella Spark elenca inoltre modelli etichettati Qwen3.8-27B e Qwen3.6-27B. Questi nomi sono qui riprodotti come etichette di benchmark dal report del contributore. La tabella delle prestazioni di per sé non stabilisce in modo indipendente i dettagli del loro addestramento, le capacità o la più ampia disponibilità.
Per la voce etichettata Qwen3.8-27B, i miglioramenti su Spark vanno dal 4,6% al 7,4%. La riga Q4_K_M sale da 840,1 a 899,9 token al secondo. La riga IQ4_XS aumenta da 882,4 a 948,0 token al secondo.
I risultati della RTX 5090 presentano una diversa scala di miglioramento. Nelle configurazioni elencate, l'autore riporta guadagni compresi tra l'1,9% e il 4,7%. Questo intervallo percentuale inferiore rende essenziale un'interpretazione specifica per l'hardware.
Per Llama-3.1-8B in IQ4_XS, il risultato della RTX 5090 aumenta da 12.997,6 a 13.603,3 token al secondo. Il miglioramento riportato è del 4,7%. Per la voce Qwen3.8-27B Q6_K, il throughput passa da 3.220,5 a 3.281,0 token al secondo, ovvero l'1,9%.
I benchmark sulla fusione di llama.cpp supportano pertanto una conclusione circoscritta. Nei test dell'autore con prompt lunghi selezionati, l'ottimizzazione migliora il throughput di input misurato nell'intera matrice riportata. Non stabiliscono un incremento identico per ogni GPU supportata.
Il confronto percentuale non rivela neppure perché Spark tragga maggior beneficio. Differenze nel comportamento della memoria, nelle caratteristiche di esecuzione e nei colli di bottiglia esistenti possono influire sui risultati dell'ottimizzazione. Stabilire il loro contributo relativo richiederebbe ulteriore profilazione, che queste sole misurazioni principali non forniscono.
Per gli acquirenti, l'implicazione utile riguarda la disciplina di valutazione. Questi risultati non giustificano la scelta dell'hardware basandosi esclusivamente sui guadagni percentuali. Throughput assoluto, adeguatezza del modello, capacità di memoria e latenza effettiva dell'applicazione restano considerazioni separate.
Le righe interlacciate eliminano lavoro tra i calcoli
La mossa centrale dell'implementazione consiste nel disporre insieme risultati correlati prima di applicare l'operazione gate.
Anujj descrive il caricamento delle righe gate e up in blocchi alternati di otto. Questa disposizione colloca i valori corrispondenti nei risultati di lavoro dello stesso thread. Il thread può quindi combinare tali valori durante la scrittura dell'output finale.
I registri, le piccole posizioni di archiviazione della GPU assegnate ai thread in esecuzione, conservano tali risultati intermedi. Applicare lì la combinazione evita un passaggio separato attraverso buffer di output intermedi per quella parte del calcolo. Il risparmio previsto deriva dall'organizzazione dell'esecuzione.
L'implementazione mantiene una disposizione di accumulatori anziché introdurre un secondo tile di accumulatori per la proiezione gate. Un accumulatore conserva somme parziali durante la moltiplicazione. Un tile è un blocco di elementi di matrice elaborati insieme.
Questa scelta progettuale distingue questa implementazione dalla precedente proposta di fusione. Secondo la descrizione del contributore, l'approccio più recente segue un suggerimento di am17an. Entrambe le proposte prendono di mira operazioni correlate, ma organizzano il calcolo in modo diverso.
La distinzione è pratica piuttosto che cosmetica. Conservare più stato intermedio può aumentare la pressione sulle limitate risorse di esecuzione di una GPU. Combinare le operazioni aiuta solo quando i risparmi superano eventuali costi aggiuntivi di pianificazione o archiviazione.
Ecco perché “meno kernel” non significa automaticamente “inferenza più veloce”. Un'operazione combinata più grande può usare l'hardware in modo meno efficiente in alcune condizioni. Un'ottimizzazione deve preservare sufficiente lavoro parallelo evitando al contempo nuovi vincoli che ne annullino i vantaggi.
Il codice integrato riflette questa preoccupazione tramite controlli di idoneità. Valuta se il carico di lavoro sia appropriato per questo percorso di fusione. Le sole operazioni matematiche supportate non garantiscono che la fusione debba essere eseguita.
L'implementazione compete inoltre con stream-k, un metodo di pianificazione che divide il lavoro di moltiplicazione matriciale lungo la sua dimensione di riduzione. Il percorso fuso non può utilizzare tale scomposizione. Deve quindi disporre di un altro modo per decidere se la sua disposizione a tile fornisca efficienza sufficiente.
Johannes Gaessler ha sollevato la questione durante la revisione, richiedendo un confronto anticipato tra l'efficienza di tiling prevista e stream-k. Il codice integrato include una decisione di efficienza prima di selezionare il percorso fuso. Ciò rende l'esecuzione condizionale parte del progetto.
La revisione della pianificazione della fusione offre la prospettiva scettica più utile sull'ottimizzazione. Si concentra sulle circostanze in cui una semplificazione allettante potrebbe perdere prestazioni. Si tratta di una limitazione tecnica, non di un disaccordo sull'esistenza del benchmark.
La competizione principale resta quindi all'interno del runtime. L'esecuzione fusa deve superare l'approccio di pianificazione esistente per la forma di operazione idonea. Questi risultati non forniscono evidenza di una vittoria più ampia su un altro framework di inferenza.
Questo meccanismo chiarisce inoltre cosa gli utenti dovrebbero aspettarsi dalla fusione CUDA di llama.cpp. Riorganizza il lavoro per un calcolo esistente. Non aumenta la capacità di ragionamento di un modello, non espande le sue conoscenze addestrate e non rimuove limiti di memoria non correlati.
Lo stesso modello svolge comunque lo stesso compito generale. L'opportunità ingegneristica consiste nell'impiegare meno tempo per raggiungere l'output di una sequenza specifica.
Un'elaborazione dei prompt più veloce modifica una parte del tempo di attesa
Il benchmark misura l'elaborazione dell'input, quindi il suo valore pratico dipende da quanto l'elaborazione dell'input contribuisca a una richiesta reale.
Il comando riportato elabora un prompt contenente 16.384 token e richiede zero token generati. I token sono le unità di testo che un modello consuma e produce. Questa configurazione isola il throughput dei prompt anziché misurare una risposta conversazionale completata.
Il comando utilizza batch e microbatch di 2.048. Un microbatch è un'unità di esecuzione più piccola ricavata da un batch più ampio. Abilita inoltre Flash Attention, un'implementazione dell'attenzione progettata per ridurre il traffico di memoria durante i calcoli di attenzione.
L'autore specifica l'archiviazione in mezza precisione per la cache chiave-valore, che conserva informazioni di attenzione provenienti da token elaborati in precedenza. Nel comando compaiono cinque ripetizioni. Queste impostazioni definiscono le condizioni testate e dovrebbero accompagnare qualsiasi risultato citato.
La metodologia di benchmark del progetto distingue l'elaborazione del prompt dalla generazione dei token. Questa separazione è fondamentale per interpretare i dati. I token di input al secondo e i token di output al secondo rispondono a domande prestazionali diverse.
Si consideri uno sviluppatore che chiede a un modello locale di analizzare un ampio file sorgente. L'elaborazione di quell'input può contribuire in modo significativo al ritardo prima che inizi la risposta. Un percorso di prefill idoneo più veloce interviene su quella parte dell'interazione.
Un breve scambio conversazionale presenta un altro carico di lavoro. Il suo prompt potrebbe richiedere relativamente poca elaborazione rispetto al tempo impiegato per generare una risposta lunga. Lo stesso miglioramento del throughput di input inciderebbe quindi su una quota minore dell'attesa complessiva.
I prompt memorizzati nella cache introducono un'ulteriore distinzione. Il caching dei prompt riutilizza il calcolo relativo a input che il sistema ha già elaborato. Quando un'applicazione riutilizza con successo quel lavoro, resta meno nuovo input che questa ottimizzazione può accelerare.
L'analisi dei documenti comprende anche attività che vanno oltre l'inferenza. Un'applicazione potrebbe recuperare file, estrarre testo, classificare passaggi o preparare un'istruzione prima di inviare qualsiasi cosa al modello. Un'elaborazione più rapida dell'input del modello non dimostra che queste fasi circostanti si concludano prima.
Per i team che gestiscono una base di conoscenza ricercabile, questa separazione favorisce decisioni migliori sulle prestazioni. La qualità del recupero e la selezione del contesto determinano ciò che raggiunge il modello. Il throughput di runtime determina la rapidità con cui vengono elaborati i calcoli idonei.
Questa distinzione esercita pressione anche sui responsabili della manutenzione delle applicazioni. Devono sapere quale runtime distribuisce il loro prodotto e se i suoi carichi di lavoro comuni raggiungono il percorso ottimizzato. Una nota di rilascio generica su un'IA locale più veloce non può rispondere a queste domande.
La risposta utile è la misurazione al confine dell'applicazione. Il tempo al primo token misura il ritardo prima dell'inizio dell'output. Il tempo di risposta totale misura l'intera interazione, mentre il throughput di generazione misura il ritmo dopo l'avvio dell'output.
Dopo un aggiornamento del runtime, ogni metrica può cambiare in modo diverso. Un miglioramento del prefill può ridurre l'attesa iniziale senza modificare la velocità del testo successivo. Riportare separatamente questi risultati offre agli utenti una descrizione più accurata dell'aggiornamento.
Per l'elaborazione dei prompt di llama.cpp, il caso d'uso più solido resta un input nuovo e consistente su una configurazione idonea. Il benchmark dell'autore pone deliberatamente l'accento su questo carico di lavoro. Estenderne il risultato a ogni sessione di chat andrebbe oltre le prove disponibili.
I formati supportati e i controlli di correttezza definiscono i confini
Il codice unito contiene restrizioni rilevanti e il resoconto dell'autore sulla correttezza dovrebbe essere letto nell'ambito della copertura di test dichiarata.
La pull request descrive il supporto per tipi di pesi Q4_K, Q5_K, Q6_K, Q8_0 e IQ4_XS su architetture NVIDIA Turing o più recenti. Queste sono famiglie di pesi supportate. I file dei modelli possono combinare tipi diversi, quindi l'etichetta complessiva di un file non descrive ogni tensore interno.
L'implementazione verifica inoltre che i pesi gate e up pertinenti soddisfino le condizioni necessarie per la fusione. Forma, tipo e layout sono importanti perché l'operazione combinata prevede calcoli corrispondenti. Una disposizione non supportata dovrebbe mantenere un altro percorso di esecuzione.
Il codice unito esclude i casi che richiedono il fallback ai confini delle righe. Un fallback gestisce forme che non riempiono in modo pulito le normali tile di elaborazione. Questa restrizione è importante perché la descrizione originale discute test che coprono i casi limite durante lo sviluppo.
I lettori dovrebbero quindi distinguere il riepilogo iniziale dei test dalle regole di selezione del codice finale. Testare un'implementazione di sviluppo più ampia non dimostra che ogni forma testata resti idonea nella versione unita. Le protezioni finali determinano l'esecuzione effettiva.
Esistono restrizioni anche sui parametri delle operazioni. Il codice verifica specifiche impostazioni di attivazione prima di selezionare la fusione. Questi controlli impediscono al runtime di trattare come intercambiabili operazioni superficialmente simili quando il comportamento richiesto differisce.
L'ambito si concentra inoltre sulle operazioni dense feed-forward. I modelli mixture-of-experts, che instradano i token attraverso sottoreti selezionate, introducono requisiti di esecuzione diversi. La revisione identifica esplicitamente ulteriore lavoro di supporto, anziché stabilire una copertura universale per questi modelli.
Gli altri backend hardware non ricevono questa ottimizzazione semplicemente perché supportano llama.cpp. Il contributore separa esplicitamente la modifica dagli altri backend. I risultati dell'hardware NVIDIA non possono quindi dimostrare benefici equivalenti su una GPU Apple o una configurazione AMD.
Le prove di correttezza sono incoraggianti, ma restano riportate dall'autore. Anujj afferma che l'intera suite di test delle operazioni backend passa su GB10. Il resoconto dichiara inoltre che le suite di moltiplicazione matriciale passano su una RTX 5090.
Il contributore riferisce inoltre risultati di perplexity identici nei test specificati con la fusione abilitata e disabilitata. La perplexity misura quanto bene un modello predice un testo di valutazione. Fornisce un utile controllo numerico, anche se non rappresenta ogni possibile comportamento applicativo.
Questo risultato sostiene un'affermazione più circoscritta rispetto all'identità universale dell'output. Punteggi identici in una valutazione dichiarata non dimostrano risposte generate identiche per tutti i prompt, le impostazioni o le piattaforme. Una convalida più ampia richiede prove più ampie.
Neppure i benchmark di fusione di llama.cpp dispongono di una riproduzione indipendente nelle fonti esaminate per questo rapporto. La revisione e l'unione a monte stabiliscono che il progetto ha accettato il codice. Non trasformano le misurazioni del contributore in un audit esterno delle prestazioni.
Nessuno di questi confini annulla il guadagno riportato. Definiscono dove l'affermazione diventa utile. Un lettore con hardware e tipi di modello corrispondenti ha una ragione concreta per effettuare test, mentre altre configurazioni richiedono prove separate.
L'interpretazione più solida combina entrambi gli aspetti. La modifica ha un meccanismo chiaro e misurazioni pubblicate, affiancati da condizioni esplicite che regolano quando viene eseguita.
Tre segnali mostreranno quanto ampiamente si diffonde il miglioramento
Le prossime prove dovrebbero collegare il codice upstream, misurazioni riproducibili e la latenza sperimentata dagli utenti.
Il primo segnale è l'adozione a valle. Nei prossimi mesi, le versioni delle applicazioni e le build distribuite dovrebbero stabilire quali prodotti includono l'implementazione unita. Questo è importante perché gli utenti interagiscono spesso con llama.cpp attraverso un'altra applicazione.
Le istruzioni di build CUDA del progetto descrivono il percorso di compilazione upstream. Le distribuzioni pacchettizzate possono seguire calendari di rilascio diversi. Un identificatore di versione associato al codice sorgente incluso è più informativo di una generica affermazione sull'uso del backend più recente.
Se applicazioni ampiamente utilizzate incorporano la modifica, il caso dell'adozione immediata si rafforza. Se restano su revisioni precedenti, la portata pratica dell'unione resta limitata. Questo esito riguarderebbe i tempi di distribuzione, non invaliderebbe l'ottimizzazione sottostante.
Il secondo segnale è una riproduzione indipendente con impostazioni esplicite. I resoconti utili dovrebbero identificare hardware, file dei modelli, revisioni del runtime e argomenti del benchmark. Dovrebbero inoltre separare l'elaborazione di prompt nuovi dall'input in cache e dall'output generato.
Riproduzioni con lunghezze di prompt diverse chiarirebbero quanto il guadagno sia sensibile alla dimensione del carico di lavoro. Resoconti su generazioni di GPU supportate diverse verificherebbero se la tendenza persiste oltre le macchine elencate. Risultati che mostrano regressioni aiuterebbero a identificare limiti nella logica di selezione.
La domanda pertinente non è se ogni esecuzione eguagli il massimo in evidenza. La domanda è se carichi di lavoro comparabili mostrino un miglioramento ripetibile con una variazione accettabile. Un guadagno minore ma costante può comunque essere utile sul piano operativo.
Il terzo segnale è la latenza dell'applicazione. I team dovrebbero misurare se un runtime aggiornato riduce l'attesa prima che inizi una risposta su richieste rappresentative. Dovrebbero mantenere il throughput di generazione e il tempo di risposta totale come misurazioni separate.
Queste prove collegherebbero la modifica del kernel all'esperienza utente. Un ritardo iniziale inferiore su prompt lunghi non memorizzati nella cache sosterrebbe la promessa pratica dell'implementazione. Un tempo di risposta totale invariato indicherebbe che altre fasi dominano il flusso di lavoro misurato.
Un supporto esteso per formati o architetture sarebbe ulteriore sviluppo, non qualcosa stabilito da questa unione. Le affermazioni future dovrebbero identificare la propria implementazione e le proprie misurazioni. I risultati attuali forniscono una base di confronto, non una conferma anticipata di tali estensioni.
Per ora, la fusione CUDA di llama.cpp offre una ragione specifica per riesaminare le prestazioni dell'inferenza locale. Punta al lavoro ripetuto all'interno di calcoli feed-forward idonei e riporta guadagni misurabili in condizioni dichiarate di elaborazione dei prompt. Questo è sufficiente a giustificare una valutazione senza promettere un aumento di velocità generalizzato.
Prima di aggiornare una distribuzione funzionante, conservate il suo identificatore di runtime attuale e un insieme rappresentativo di richieste. Confrontate gli stessi file dei modelli, le lunghezze dei prompt, le condizioni della cache e le impostazioni di output dopo l'aggiornamento. Registrate separatamente il tempo di attesa iniziale dalla generazione della risposta e conservate gli argomenti del benchmark insieme a ogni risultato. Per i flussi di lavoro su documenti lunghi, includete input nuovi che assomiglino all'uso quotidiano anziché basarvi interamente sul throughput sintetico. Per i carichi di lavoro conversazionali, verificate se un minore ritardo nell'elaborazione dell'input modifica l'esperienza in misura sufficiente da essere rilevante. Quindi usate le vostre misurazioni per decidere dove collocare il miglioramento upstream nel vostro calendario di rilascio. Il runtime aggiornato riduce davvero la parte della richiesta che i vostri utenti attendono?



