Vai al contenuto
Nicola Sabaini

Articoli

Su otto giga di VRAM non conta quanto è grande il modello

Un 27B denso gira a 2,5 token al secondo. Un MoE da 35 miliardi, sulla stessa scheda, ne fa trenta. La differenza non è la taglia.

4 min di lettura

Il piano era far girare Qwen3.8-27B su una RTX 3070 Ti da 8 GB. Ha girato: 2,5 token al secondo. Alla velocità con cui si legge una frase in quindici secondi, un agente che deve scrivere codice non è uno strumento, è una penitenza.

La reazione istintiva è mettersi a tarare. L’ho fatto, per bene, misurando tutto. Margine di VRAM più stretto, numero di thread, dimensione dei batch, speculazione da ngram, una testa MTP, un quant più piccolo.

Da 2,29 a 2,48 token al secondo. Otto per cento.

Poi ho cambiato modello. Non taglia, forma. E sono passato a trenta.

Il numero che conta non è quello sulla scatola

modello parametri attivi per token generazione
Qwen3.8-27B UD-Q3_K_XL 27 B 27 B 2,5 t/s
Qwen3.6-35B-A3B Q4_K_M 35 B 3 B ~30 t/s

Il modello più grande va dodici volte più veloce. Non è un paradosso: è l’unica cosa che poteva succedere.

Su 8 GB di VRAM nessuno dei due ci sta. Metà dei pesi vive sulla DDR4, e la DDR4 di questa macchina rende circa 14,5 GB/s utili contro i 608 della scheda. Quarantadue volte meno. Quel bus è il collo di bottiglia, e l’unica domanda che conta diventa: quanti byte devo farci passare per ogni token?

Ventisette miliardi di pesi vanno letti tutti, ogni volta, per produrre una parola sola. Il MoE ne attiva tre su trentacinque: i pesi degli esperti stanno in RAM, ma per token se ne legge una frazione, e l’attenzione resta in GPU dove c’è la banda.

La quantità di parametri è una proprietà del file. La quantità di parametri attivi è una proprietà dell’architettura, ed è quella che il tuo bus paga.

Tre cose che davo per scontate ed erano sbagliate

I thread vanno in direzioni opposte fra le due architetture.

modello 6 thread 12 thread
27B denso 2,16 t/s 2,48 t/s
35B-A3B MoE 30,6 t/s 22,9 t/s

Su llama.cpp la regola tramandata è fermarsi ai core fisici. Sul denso è falsa: il lavoro è dominato dalla latenza di dequantizzazione, e l’SMT riempie le bolle. Sul MoE è vera e anche di più, perché per token si attiva poca roba, il lavoro per thread è poco e sincronizzare dodici thread costa più di quanto renda.

Avevo tarato dodici thread sul denso e me li ero portati dietro sul MoE. Mi costavano il venticinque per cento. Ora il launcher sceglie da solo: sei sul MoE, dodici sul denso.

Il quant più piccolo è più lento. Q2_K_XL pesa tre gigabyte in meno del Q3, e infatti si vede: legge il prompt a 354 token al secondo invece di 250, perché ne sta di più in GPU. Poi genera a 1,52 invece di 2,48. Il percorso di dequantizzazione a due bit costa alla CPU molto più di quanto si risparmi in byte da trasferire. Meno byte, più tempo.

La testa MTP è in perdita. 1,80 contro 2,48. Si prende 1,37 GB di VRAM, e su otto giga quella VRAM la stavi già usando per i pesi. Su una scheda più capiente il conto si ribalta, ma il conto va fatto sulla scheda che hai.

E una quarta, che è una non-scoperta e vale lo stesso: quanti esperti tenere in VRAM non cambia niente. Da 28 a 64, tutto fra 29,66 e 30,70 t/s, cioè dentro il rumore. Allora tanto vale il valore che lascia più VRAM libera per il contesto.

“Veloce” non è una risposta se il modello sbaglia

C’è un modello ancora più rapido: qwen3.5 da 9 miliardi, via Ollama, ci sta tutto in VRAM, sessantacinque token al secondo. Il doppio del MoE.

Li ho messi davanti allo stesso compito: trovare un bug non ovvio in una merge_intervals. Il bug c’è, ed è che la funzione muta la lista del chiamante, perché la lista in uscita contiene riferimenti agli stessi oggetti di quella in ingresso.

Il 35B-A3B lo trova, e spiega che l’assegnamento su last[1] sta scrivendo dentro l’oggetto originale. Corretto.

Secondo il 9b il controllo iniziale if not intervals: restituisce lista vuota quando l’input ha un solo elemento. È falso, not [x] è False, e non è un dettaglio: si è inventato un bug che non c’è e non ha visto quello che c’è.

Per il codice ripetitivo e per le risposte che devono arrivare subito il 9b resta ottimo. Ma “veloce” e “utile” sono due assi diversi, e per ragionare sul codice il primo non compra il secondo.

La regola

Se la GPU non basta a tenere il modello, smetti di chiederti quanto è grande e comincia a chiederti quanto ne è attivo. Poi misura sulla macchina che hai, non su quella dei benchmark di qualcun altro: qui tre monitor accesi costavano seicentocinquanta megabyte di framebuffer, che a otto giga sono un ottavo della scheda.

Ho ancora installato il 27B. È l’unico dei due ad avere l’encoder per le immagini, e per quello va benissimo: lento, ma è l’unico che ci vede.

Tutti gli articoli