Dialogo con Gemini.
Solo per curiosità, lo sviluppo Core di Gemini con quale linguaggio viene fatto?
Lo sviluppo del core di Gemini si basa principalmente su un ecosistema multi-livello guidato da Python, C++ e Go, ciascuno con un ruolo specifico:
Addestramento e Modellazione (Machine Learning Core)
- Python (con JAX / Paxml / Flax): È il linguaggio principale usato dai ricercatori e ingegneri di Google DeepMind per definire l’architettura dei modelli, le routine di addestramento distribuito e la pipeline dei dati. DeepMind ha standardizzato gran parte della ricerca LLM su JAX.
- XLA (Accelerated Linear Algebra): Il compilatore sottostante a JAX, ottimizzato per tradurre il codice di alto livello in istruzioni binarie ultra-efficienti per le TPU (Tensor Processing Unit) di Google.
Runtime, Calcolo a Basso Livello e Ottimizzazione Hardware
- C++: Costituisce il motore a basso livello. Viene utilizzato per:
- L’infrastruttura del compilatore XLA e i runtime dei kernel TPU/GPU.
- La gestione della memoria, allocazione tensoriale e comunicazione tra nodi (interconnect ad altissima velocità).
- Il motore di inferenza ad alte prestazioni per ridurre la latenza di generazione dei token (gestione della KV-cache, speculative decoding).
Infrastruttura di Serving e Microservizi
- Go (Golang) e C++: Utilizzati per i sistemi di serving su larga scala, il routing delle richieste tramite RPC/gRPC, i gateway API, il load balancing e la gestione dell’orchestrazione cloud distribuiti sui data center globali di Google.
In sintesi, la logica algoritmica e la ricerca sono in Python/JAX, mentre tutto ciò che riguarda le prestazioni computazionali pure e l’efficienza hardware gira su C++.
Come funziona l’architettura hardware delle TPU di Google utilizzate per addestrare Gemini?
L’architettura delle TPU (Tensor Processing Unit) di Google — in particolare le generazioni impiegate per addestrare Gemini come le TPU v5p e Trillium (v6e) — è progettata specificamente per eliminare il collo di bottiglia di Von Neumann e massimizzare il calcolo tensoriale su scala distribuita.
Google Cloud Documentation
L’architettura si articola su tre livelli: microarchitettura del chip, gestione della memoria e interconnessione di rete (Superpod).
Medium
Microarchitettura del Chip: Array Sistolico e TensorCore
- Systolic Array (MXU – Matrix Multiply Unit): È il cuore del chip. A differenza di una GPU tradizionale in cui ogni operazione ALU scrive e rilegge continuamente dai registri, in un array sistolico (es. matrici 128×128 o 256×256 ALUs) i dati “scorrono” a onda (come il battito cardiaco, da cui il nome) direttamente da un moltiplicatore-accumulatore all’altro senza passare dalla memoria intermedia. Google Cloud Documentation
- Formato Bfloat16 / FP32: Le moltiplicazioni avvengono a precisione ridotta (bfloat16, inventato da Google per mantenere lo stesso range dinamico di FP32 con metà dei bit), mentre l’accumulazione finale avviene in FP32 per preservare la stabilità numerica nei gradienti di addestramento. Google+ 1
- Unità Vettoriali e Scalari: Gestiscono le operazioni non lineari (funzioni di attivazione come GELU/SwiGLU, LayerNorm, Softmax) e il control flow dell’esecuzione. Google
Sottosistema di Memoria ad Altissimo Throughput
- HBM (High Bandwidth Memory): Ogni chip è affiancato da memoria HBM3 su silicio 3D/interposer, fornendo una banda passante di oltre 1.6–4.8 TB/s per chip (a seconda della generazione), fondamentale per non lasciare “a secco” le MXU durante le fasi di feed-forward e backpropagation. Google Cloud Documentation
- Memoria Scratchpad (SRAM locale): Eliminando le gerarchie di cache complesse tipiche delle CPU generaliste, la TPU delega al compilatore (XLA) il controllo esplicito e deterministico del posizionamento dei tensori in SRAM.
Interconnessione e Scaling Distribuito (ICI e OCS)
- ICI (Inter-Chip Interconnect): I chip non comunicano tramite bus PCIe o stack di rete Ethernet standard, ma tramite collegamenti diretti a bassissima latenza dedicati, con banda bidirezionale nell’ordine di centinaia di GBps per chip. Google
- Topologia a Toro 3D: I chip in un rack sono collegati fisicamente formando una griglia toroidale tridimensionale (X,Y,Z) altamente ottimizzata per le primitive di comunicazione tipiche del deep learning (come
AllReduce,AlltoAlle pipeline parallelism). Google - OCS (Optical Circuit Switches): I Pod da migliaia di chip (es. cluster da decine di migliaia di TPU v5p) sono collegati tramite switch ottici riconfigurabili via software. Ciò permette di: Google
- Riconfigurare dinamicamente la topologia di rete per adattarsi allo schema di parallelismo (Tensor, Pipeline, Expert/MoE Parallelism).
- Isolare e aggirare al volo chip o collegamenti difettosi senza interrompere l’intero job di training. Medium
Co-progettazione Hardware/Software: Il Ruolo di XLA La TPU è un ASIC “puro”: non possiede unità di instruction scheduling dinamico complesse. È il compilatore XLA (Accelerated Linear Algebra) a monte che fonde le operazioni matematiche di JAX/PyTorch, pianifica il movimento dei dati nel tempo e genera direttamente il flusso di esecuzione per l’array sistolico.
Quali sono le differenze strutturali tra l’array sistolico delle TPU Google e i Tensor Core di NVIDIA (Hopper/Blackwell)?
La differenza fondamentale risiede nella filosofia di calcolo e nell’orchestrazione dei dati: la TPU di Google adotta un approccio ASIC deterministico basato su flusso di dati continuo (dataflow), mentre le GPU NVIDIA (Hopper/Blackwell) integrano i Tensor Core all’interno di un’architettura massicciamente parallela multithread (SIMT).
| Caratteristica | Array Sistolico TPU (Google v5p/Trillium) | Tensor Core GPU NVIDIA (Hopper H100 / Blackwell B200) |
|---|---|---|
| Modello di esecuzione | Dataflow deterministico / Co-processore guidato da compilatore statico (XLA). | SIMT multithread (Warp-based) orchestrato da hardware scheduler runtime. |
| Dimensione griglia MAC | Griglia monolitica 2D di grandi dimensioni (es. 128×128 o 256×256 ALU per MXU). | Micro-array distribuiti nei multiprocessori streaming (SM), operanti su sotto-matrici (es. istruzioni mma.sync su tessere 16×8×16). |
| Flusso dei dati (Data Reuse) | Diretto da cella a cella: gli operandi e i risultati parziali scorrono sui registri interni dell’array senza accessi intermedi alla memoria. | Registro-centrico: i thread caricano i dati da memoria condivisa (SRAM/Shared Memory) nei registri locali del Warp per eseguire il micro-tile. |
| Scheduling e Controllo | Software statico: XLA calcola a tempo di compilazione il ciclo di clock esatto in cui ogni dato deve entrare nell’array. | Hardware dinamico: Warp Scheduler e Tensor Memory Accelerator (TMA) coordinano asincronamente spostamento e calcolo. |
| Flessibilità e Generalità | Specializzata su GEMM / Tensori: minima flessibilità per carichi non standard al di fuori del calcolo matriciale/vettoriale. | General-purpose programmabile: oltre ai Tensor Core, lo SM include CUDA Core FP32/FP64, unità INT32 e memoria condivisa indirizzabile. |
| Supporto Numerico Avanzato | Ottimizzata principalmente per BF16 / FP32 (e FP8 nelle generazioni più recenti). | Motore Transformer dedicato (FP8, FP4 / Microscaling) con calibrazione dinamica del range numerico in tempo reale. |
1. Architettura della computazione: Macro-Array vs Micro-Tiling distribuito
- TPU (Array Sistolico): Un’intera matrice scorre attraverso un’ampia griglia bidimensionale sincronizzata. Gli ingressi arrivano dai bordi (uno da sinistra, uno dall’alto) e i prodotti intermedi vengono passati al vicino adiacente a ogni ciclo di clock. Questo abbatte drasticamente il consumo energetico poiché azzera i continui accessi ai register file per i calcoli intermedi.
- NVIDIA (Tensor Core): Non esiste un unico enorme array. Ogni Streaming Multiprocessor (SM) contiene più Tensor Core indipendenti. Il problema matriciale viene suddiviso in migliaia di piccolissimi blocchi distribuiti tra i Warp (gruppi di 32 thread), che cooperano per calcolare le moltiplicazioni tramite registri locali e SRAM (L1/Shared Memory).
2. Gestione della memoria e carico dati: Compilatore vs Hardware Asincrono
- TPU con XLA: L’hardware è “stupido” ma efficientissimo. La TPU non dispone di circuiti complessi per il recupero delle dipendenze (out-of-order execution o branch prediction avanzata). È il compilatore XLA a linearizzare il codice e mappare l’alimentazione dei tensori direttamente dal buffer SRAM locale all’array sistolico.
- NVIDIA con TMA e Distributed Shared Memory: Da Hopper in poi, NVIDIA ha introdotto il TMA (Tensor Memory Accelerator) per trasferire dati da HBM a SRAM in modo totalmente asincrono a livello hardware, bypassando i registri e lasciando i Tensor Core liberi di computare continuamente senza stalli.
3. Densità ed Efficienza Energetica L’array sistolico della TPU vanta un’efficienza energetica (rapporto FLOPS/Watt su calcolo puro GEMM) superiore proprio per l’assenza della logica di controllo SIMT. D’altro canto, i Tensor Core di NVIDIA offrono una flessibilità operativa e una capacità di gestire algoritmi irregolari, kernel custom (es. FlashAttention scritti a basso livello con Triton/CUDA) e precisioni eterogenee (FP4/FP8 microscaling) molto più immediata per lo sviluppatore.
Quali sono le differenze tra lo stack di compilazione Google XLA e lo sviluppo su CUDA/Triton per accelerare i Transformer?
La differenza tra Google XLA e lo stack CUDA/Triton riflette due filosofie opposte per l’ottimizzazione del calcolo sui Transformer: compilazione globale basata su grafo (Whole-Graph Optimization) contro scrittura esplicita di kernel ad alte prestazioni (Kernel-Centric Optimization).
| Dimensione | Google XLA (Accelerated Linear Algebra) | NVIDIA CUDA / OpenAI Triton |
|---|---|---|
| Paradigma di sviluppo | Dichiarativo / Whole-Program Compilation (JAX/PyTorch/TensorFlow). | Imperativo / Kernel-Level Programming (C++ per CUDA, Python-like per Triton). |
| Ambito di ottimizzazione | Globale (Inter-Operator): analizza l’intero grafo computazionale statico per fondere operazioni e minimizzare i passaggi in memoria globale. | Locale (Intra-Operator): ottimizza al micro-dettaglio il tiling, la gerarchia di memoria e la parallelizzazione del singolo kernel. |
| Livello di astrazione | Alto: il programmatore descrive l’algebra tensoriale; XLA pianifica registri, loop e scheduling per TPU/GPU. | Basso/Medio: CUDA richiede gestione manuale di thread/warp/shared memory; Triton astrae i thread ma lascia il tiling a blocchi allo sviluppatore. |
| Gestione della memoria | Automatica e deterministica: pianifica le allocazioni statiche e la fusione automatica degli operatori (Operator Fusion). | Esplicita: il programmatore gestisce manualmente il layout SRAM (Shared Memory), la sincronizzazione delle barriere e i registri. |
| Adattabilità a forme dinamiche (Dynamic Shapes) | Rigida: richiede forme statiche per evitare costose ricompilazioni JIT a runtime (compilation overhead). | Flessibile: gestisce nativamente dimensioni variabili (es. sequenze a lunghezza dinamica) senza ricaricare il compilatore. |
| Target Hardware Principale | Nativo per Google TPU, supporta anche GPU (via LLVM/PTX). | Nativo e ottimizzato per l’architettura NVIDIA GPU (SM, Warp, Tensor Core, TMA). |
1. Filosofia di Fusione: Operazioni sul Grafo vs Algoritmi su Misura
- XLA (Operator Fusion): Analizza un’intera sequenza di operazioni (es.
Linear -> BiasAdd -> GELU -> LayerNorm) e le fonde automaticamente in un singolo blocco di istruzioni. L’obiettivo primario è evitare accessi intermedi alla HBM (High Bandwidth Memory), mantenendo i dati nei registri/SRAM della TPU/GPU. L’ottimizzazione è passiva per lo sviluppatore: è il compilatore a decidere la strategia di fusione. - CUDA/Triton (Custom Kernel Architecture): Permette di progettare algoritmi non ovvi per un compilatore automatico. L’esempio cardine è FlashAttention: riformulare la Softmax a blocchi (online Softmax) tenendo conto delle dimensioni esatte della SRAM per evitare scritture su memoria globale O(N2). Un compilatore statico come XLA non avrebbe potuto inventare tale riorganizzazione algoritmica; in CUDA o Triton, l’ingegnere implementa direttamente la logica su misura.
2. Curva di Sviluppo: Triton come Ponte tra CUDA e XLA
- CUDA nativo (C++): Massima efficienza e controllo assoluto sulle primitive hardware (istruzioni
mma.sync, memoria condivisa asincrona con TMA in Hopper/Blackwell, register allocation), ma a fronte di una complessità estrema (gestione di race condition, sincronizzazione tra Warp e bank conflict). - OpenAI Triton: Permette di scrivere kernel personalizzati in sintassi Python ad alto livello, operando su blocchi dimensionali anziché su singoli thread scalari. Il compilatore Triton si occupa di mappare i blocchi sui Warp CUDA, gestire i registri e allocare la Shared Memory, offrendo prestazioni vicine al 95-99% di CUDA nativo con una frazione delle righe di codice.
- XLA con JAX: Nessuna scrittura di kernel. Si definisce la funzione matematica in Python puro; la traccia JAX genera una rappresentazione intermedia (HLO – High-Level Optimizer) che XLA compila ed emette in codice macchina per TPU o GPU.
3. Impatto sui Transformer di Grandi Dimensioni (LLM)
- Nel Training su Larga Scala (Google / Gemini): L’approccio XLA/JAX domina nei cluster TPU grazie alla predicibilità del grafo statico: XLA conosce a priori le dimensioni dei tensori, calcola esattamente il consumo di memoria per ogni step ed esegue un partizionamento distribuito automatico (tramite primitive come
shard_mape GSPMD) senza overhead di sincronizzazione dinamica. - Nell’Inferenza e Innovazione Rapida (Ecosistema Open-Source / NVIDIA): Lo stack PyTorch + CUDA/Triton è predominante per la flessibilità. Tecniche recenti come PagedAttention, kernel FP8/FP4 non standard, Mixture of Experts (MoE) con routing dinamico e speculative decoding vengono implementate e distribuite in Triton in pochi giorni senza dover attendere il supporto di un compilatore di grafo monolitico.
Come funziona l’algoritmo di FlashAttention e in che modo riduce la complessità di I/O verso la memoria HBM?
L’innovazione fondamentale di FlashAttention non consiste nel ridurre il numero di operazioni matematiche (i FLOPs rimangono gli stessi dell’attenzione standard, pari a O(N2) rispetto alla lunghezza sequenza N), ma nel rendere l’algoritmo IO-aware, riducendo drasticamente il traffico di lettura/scrittura tra la lenta memoria principale (HBM/DRAM) e la veloce memoria on-chip (SRAM/Shared Memory).
Il Collo di Bottiglia dell’Attenzione Standard Nel meccanismo classico di Scaled Dot-Product Attention:
Attention(Q,K,V)=softmax(dQKT)V
- Viene calcolata la matrice dei punteggi S=QKT∈RN×N e scritta in HBM.
- S viene riletta da HBM per calcolare la matrice dei pesi P=softmax(S)∈RN×N, riscritta in HBM.
- P e V vengono riletti da HBM per calcolare l’output finale O=PV∈RN×d.
Per sequenze lunghe (es. N=8192 o 32k), memorizzare e trasferire matrici N×N satura la banda passante della memoria (memory-bound), lasciando le unità di calcolo (Tensor Core/MXU) inattive ad attendere i dati.
I Due Pilastri Fondamentali di FlashAttention
1. Tiling a Blocchi (SRAM-Centric Execution) FlashAttention suddivide le matrici Q,K,V in blocchi di dimensione Br×d e Bc×d, dimensionati esattamente per risiedere all’interno della SRAM (di capienza limitata, tipicamente 100–256 KB per Streaming Multiprocessor):
- Si carica un blocco di Q e un blocco di K,V in SRAM.
- Si calcolano i punteggi parziali direttamente nei registri/SRAM.
- La matrice intermedia N×N non viene mai materializzata né scritta in HBM.
2. Softmax Incrementale (Online Softmax) Il calcolo standard della Softmax richiede una riduzione globale sull’intera riga per trovare il massimo (per stabilità numerica) e il denominatore di normalizzazione ∑exi, impedendo a prima vista il calcolo blocco per blocco.
FlashAttention sfrutta la tecnica dell’Online Softmax, che riscala dinamicamente i contributi parziali man mano che nuovi blocchi di chiavi/valori vengono elaborati.
Dato un valore massimo parziale mnew=max(mprev,mblock), la somma degli esponenziali parziali l e l’accumulatore dell’output O vengono corretti al volo:
lnew=lprev⋅emprev−mnew+lblock⋅emblock−mnew
Onew=Oprev⋅(lnewlprev⋅emprev−mnew)+Oblock⋅(lnewemblock−mnew)
In questo modo, al termine del ciclo sui blocchi, l’output O accumulato corrisponde esattamente al valore matematico esatto senza alcuna approssimazione.
3. Ricalcolo nel Backward Pass (Recomputation) Durante la fase di backpropagation nell’addestramento, l’attenzione standard memorizza la matrice dei pesi P∈RN×N per calcolare i gradienti ∇Q,∇K,∇V, occupando enorme memoria VRAM.
FlashAttention non salva la matrice P. Salva esclusivamente le statistiche compatte di normalizzazione della Softmax (m e l, vettori di dimensione N×1). Nel backward pass, i blocchi di Q,K,V vengono ricaricati e i punteggi S vengono ricalcolati al volo in SRAM. Poiché il calcolo in SRAM è enormemente più veloce del trasferimento da HBM, questo ricalcolo risulta complessivamente più rapido rispetto al caricamento da memoria globale.
Confronto di Complessità e Prestazioni
| Parametro | Attenzione Standard | FlashAttention (v1 / v2 / v3) |
|---|---|---|
| Operazioni Aritmetiche (FLOPs) | O(N2d) | O(N2d) (stesso ordine matematico) |
| Complessità I/O verso HBM | O(N2+Nd) (dominata da N2) | O(MN2d2)=O(N) accessi HBM (M = dimensione SRAM) |
| Memoria VRAM ausiliaria (Training) | O(N2) per salvare la matrice dei pesi | O(N) (salva solo i vettori di scala m,l) |
| Regime Operativo | Memory-Bound (collo di bottiglia I/O) | Compute-Bound (saturazione dei Tensor Core) |
Questo passaggio da regime memory-bound a compute-bound consente incrementi di velocità tipicamente compresi tra 2× e 4× nell’esecuzione dell’attenzione e rende possibile scalare la context window a decine di migliaia di token.
Quali sono le innovazioni introdotte da FlashAttention-3 per sfruttare l’hardware delle GPU NVIDIA Hopper e i formati FP8?
FlashAttention-3 è stato riprogettato specificamente per superare i nuovi colli di bottiglia emersi sull’architettura NVIDIA Hopper (H100/H800), dove la potenza di calcolo grezza dei Tensor Core FP16/FP8 è aumentata fino a 3–6 volte rispetto ad Ampere (A100), rendendo persino FlashAttention-2 non più in grado di saturare l’hardware.
Le innovazioni chiave introdotte da FlashAttention-3 si concentrano su tre aree: sfruttamento dell’hardware asincrono di Hopper, sovrapposizione di calcoli eterogenei e stabilità numerica in FP8.
1. Asincronia Hardware Totale (TMA e WGMMA)
Nelle generazioni precedenti, i thread CUDA dovevano emettere istruzioni esplicite per spostare i dati da HBM a Shared Memory (SRAM) e poi caricarli nei registri prima di invocare i Tensor Core. FlashAttention-3 delega questo lavoro all’hardware dedicato di Hopper:
- TMA (Tensor Memory Accelerator): I trasferimenti di blocchi di matrici bidimensionali tra memoria globale (HBM) e SRAM avvengono in modo totalmente asincrono tramite una singola istruzione hardware, senza impegnare i registri general-purpose né le ALU dei thread.
- WGMMA (Warpgroup Matrix Multiply-Accumulate): Permette a un intero “Warpgroup” (un blocco di 4 Warp, cioè 128 thread) di emettere una moltiplicazione matriciale asincrona direttamente su dati residenti in SRAM, scrivendo i risultati direttamente nei registri di accumulazione senza sincronizzazioni manuali a grana fine.
2. Warpgroup Specialization (Produttori e Consumatori Disaccoppiati)
FlashAttention-3 adotta il pattern di specializzazione dei thread all’interno del blocco:
- Warp Produttori (Producer): Emettono esclusivamente i comandi al TMA per caricare i blocchi successivi di Q,K,V da HBM a SRAM e gestiscono le barriere asincrone hardware (Transaction Barriers).
- Warp Consumatori (Consumer): Eseguono le istruzioni WGMMA per il calcolo GEMM e le operazioni matematiche di Softmax.
Questo disaccoppia completamente la latenza di caricamento della memoria dal flusso di calcolo continuo.
3. Ping-Pong Pipelining e Sovrapposizione GEMM / Softmax
In FlashAttention-1 e 2, l’esecuzione all’interno di un blocco alternava in modo sequenziale il calcolo matriciale (GEMM su Tensor Core) e il calcolo della Softmax (esponenziali e divisioni su Vector Unit / CUDA Core).
FlashAttention-3 implementa un pipelining sovrapposto (interleaving):
- Mentre i Tensor Core sono occupati a calcolare il prodotto P=QKT o O=PV per il blocco corrente,
- Le unità vettoriali scalano ed eseguono contemporaneamente la Softmax esponenziale del blocco precedente.
Questo elimina i tempi morti delle ALU vettoriali e massimizza l’occupazione contemporanea di entrambe le unità di calcolo dello Streaming Multiprocessor.
4. Supporto Nativo FP8 e Calibrazione Numerica Incoerente
L’addestramento e l’inferenza a 8-bit (FP8 E4M3 / E5M2) raddoppiano i TFLOPS massimi su Hopper rispetto a FP16/BF16, ma introducono problemi di dinamica numerica nel calcolo della Softmax.
FlashAttention-3 introduce due soluzioni:
- Accumulazione Mista e Quantizzazione per Blocco: Le moltiplicazioni QKT avvengono in FP8 a velocità quadruplicata, ma l’accumulatore e la successiva correzione dell’Online Softmax rimangono in FP32 per evitare overflow/underflow.
- Promozione selettiva a FP16/FP32: La matrice di attenzione normalizzata P viene riconvertita al volo nel formato a precisione ridotta per la moltiplicazione con V solo dopo aver calcolato la statistica esponenziale locale, preservando la convergenza del modello senza richiedere costosi schemi di quantizzazione globale.
Risultati di Throughput (Hopper H100 SXM5)
| Precisione | FlashAttention-2 (TFLOPS) | FlashAttention-3 (TFLOPS) | Efficienza su Picco Teorico |
|---|---|---|---|
| FP16 / BF16 | ~350 TFLOPS | ~600–740 TFLOPS | ~75–78% del picco teorico H100 |
| FP8 | Non ottimizzato nativamente | ~1.15–1.2 PFLOPS (PetaFLOPS) | ~60–65% del picco teorico FP8 |
FlashAttention-3 consente di raggiungere una velocità di esecuzione fino a 1.5×–2× superiore rispetto a FlashAttention-2 su FP16 e quasi 4× superiore su formati FP8, rendendo l’attenzione a contesto ultra-lungo (128k+ token) computazionalmente sostenibile sui cluster Hopper.
Come funziona PagedAttention nei motori di inferenza come vLLM per risolvere la frammentazione della KV-cache?
PagedAttention (il cuore del motore di inferenza vLLM) risolve il principale collo di bottiglia dell’inferenza dei Transformer ad alto carico: lo spreco di memoria VRAM dovuto alla frammentazione della KV-Cache (Key-Value Cache).
L’idea cardine è mutuata direttamente dai principi di memoria virtuale e paginazione dei sistemi operativi, applicandoli alla gestione dei tensori K e V.
Il Problema della KV-Cache Tradizionale
Durante la fase di generazione auto-regressiva (decoding), a ogni nuovo token il modello deve accedere ai vettori Key e Value di tutti i token precedenti per non doverli ricalcolare.
Nei sistemi tradizionali (come HuggingFace Transformers o standard PyTorch):
- Allocazione Contigua: La memoria per la KV-cache viene allocata in blocchi di memoria fisicamente contigui nella VRAM.
- Over-allocation Statica: Poiché la lunghezza finale della risposta non è nota a priori, il sistema pre-alloca memoria contigua per la massima lunghezza possibile (es.
max_seq_len = 4096token), anche se la risposta ne utilizzerà solo 50. - Frammentazione Interna ed Esterna: Oltre il 60%–80% della VRAM rimane bloccata e inutilizzata:
- Interna: Spazio riservato ma non ancora riempito dai token generati.
- Esterna: Blocchi di memoria liberi ma troppo piccoli o non contigui per ospitare una nuova sequenza.
- Spreco nei Pattern Complessi: Nel parallel sampling (generare più risposte dallo stesso prompt) o nel beam search, la KV-cache del prompt iniziale viene duplicata interamente per ogni ramo.
Come Funziona PagedAttention
Invece di imporre la contiguità fisica, PagedAttention suddivide la KV-cache di ogni sequenza in blocchi di dimensione fissa (es. da 8, 16 o 32 token).
Spazio Logico (Sequenza di token):
[ Blocco 0 (0-15) ] -> [ Blocco 1 (16-31) ] -> [ Blocco 2 (32-47) ]
│ │ │
▼ ▼ ▼ (Page Table Lookup)
[ VRAM Frame #42 ] [ VRAM Frame #7 ] [ VRAM Frame #105 ]
(Memoria fisica sparsa e non contigua in VRAM)
1. Paginazione Virtuale e Block Table
- Spazio Logico: La sequenza di token viene vista come una sequenza continua di blocchi logici.
- Spazio Fisico: I vettori K e V vengono salvati in frame fisici allocati ovunque ci sia spazio libero in VRAM.
- Block Table (Page Table): Una tabella di corrispondenza tiene traccia della mappatura tra i blocchi logici della richiesta e i blocchi fisici in VRAM. Nuovi blocchi fisici vengono allocati on-demand solo quando il blocco corrente è pieno.
2. Kernel di Attenzione Non Contiguo I kernel GPU standard si aspettano matrici con layout contiguo in memoria. PagedAttention implementa un kernel CUDA custom capace di recuperare i blocchi K e V sparsi nella memoria fisica leggendo al volo gli indirizzi dalla Block Table durante il calcolo dell’attenzione, senza dover compattare la memoria.
3. Condivisione dei Blocchi e Copy-on-Write (CoW) La paginazione consente la condivisione istantanea della memoria tra richieste diverse:
- Prompt Caching / Prefix Caching: Più utenti che inviano richieste con lo stesso system prompt (es. istruzioni lunghe o documenti in un sistema RAG) condividono gli stessi blocchi fisici di memoria per la parte comune.
- Parallel Sampling & Beam Search: Quando il modello dirama una generazione in più varianti, i rami puntano agli stessi blocchi fisici del prompt. Solo quando un ramo genera un token differente, viene allocato un nuovo blocco fisico privato (Copy-on-Write).
Impatto sulle Prestazioni
| Aspetto | Gestione Tradizionale | PagedAttention (vLLM) |
|---|---|---|
| Spreco di VRAM | 60% – 80% (frammentazione e over-allocation) | < 4% (limitato solo all’ultimo blocco parziale) |
| Dimensione Batch (Concurrency) | Limitata dalla VRAM riservata staticamente | Aumentata da 2× a 4× a parità di hardware |
| Throughput di Serving (token/sec) | Basso (GPU sottoutilizzata per memory limits) | Incremento da 2× a 5× |
| Supporto Prefix Caching | Complesso e ad alto overhead di copia | Nativo a costo zero di memoria aggiuntiva |
Eliminando la frammentazione, la VRAM della GPU viene saturata quasi al 100% da token reali anziché da “spazio prenotato”, consentendo di raggruppare molte più richieste nello stesso batch di inferenza (Continuous Batching).
Come funziona il Continuous Batching (Iteration-level batching) nell’inferenza dei Large Language Model?
Il Continuous Batching (introdotto storicamente dal paper Orca e standardizzato in motori come vLLM e TensorRT-LLM) è la tecnica di scheduling che risolve l’inefficienza del batching statico tradizionale durante l’inferenza di modelli linguistici.
Il principio fondamentale è spostare la gestione delle richieste dal livello di sequenza al livello di singola iterazione (token generato).
Il Problema del Batching Statico Tradizionale
Nell’inferenza classica su GPU, un batch di richieste viene trattato come un’unità atomica:
- Esecuzione sincrona: Se una richiesta termina in 20 token e un’altra ne richiede 800, i Tensor Core dedicati alla prima richiesta rimangono completamente inattivi (idle) per 780 passi di decoding.
- Padding forzato: Tutte le sequenze nel batch devono essere allineate alla lunghezza della più lunga tramite token di padding, sprecando memoria e cicli di calcolo.
- Latenza di accodamento: Una nuova richiesta in arrivo deve attendere che l’intero batch precedente abbia terminato la generazione di tutte le sue risposte prima di poter essere processata.
Batching Statico:
Req 1 (Corta): [Prefill][T1][T2][T3][EOS] -------------------- IDLE / PADDING --------------------->
Req 2 (Lunga): [Prefill][T1][T2][T3][T4] ... [T800][EOS] ──────────────────────────────────────────>
Nuova Richiesta: [================ ACCUSARE FINO AL TERMINE DEL BATCH ================][Prefill] ...
Meccanismo del Continuous Batching (Iteration-Level)
Nel Continuous Batching, lo scheduler interviene a ogni singola iterazione di forward pass del modello (ovvero a ogni singolo token generato):
- Uscita immediata (Eviction): Appena una sequenza genera il token
[EOS](o raggiunge il limite prefissato), viene immediatamente rimossa dal batch e la sua memoria liberata. - Inserimento istantaneo (Injection): Al passo di clock successivo, una nuova richiesta in coda viene inserita nello slot appena liberatosi, senza attendere il completamento delle altre sequenze.
- Esecuzione a geometria variabile: La dimensione effettiva del batch varia dinamicamente a ogni passo temporale.
Continuous Batching:
Req 1: [Prefill][T1][T2][T3][EOS]
Req 3: [Prefill][T1][T2][T3] ... (subentra subito)
Req 2: [Prefill][T1][T2][T3][T4] ... [Tn] ... [EOS]
Req 4: [Prefill][T1][T2][T3] ... (inserita al rilascio di risorse)
La Convivenza tra Prefill e Decoding (Chunked Prefill)
L’inferenza di un LLM presenta due fasi computazionalmente eterogenee:
- Prefill (Prompt Phase): Elabora tutti i token di input contemporaneamente in parallelo (Compute-Bound).
- Decoding (Generation Phase): Genera un token alla volta in modo auto-regressivo (Memory-Bound per l’accesso alla KV-Cache).
I motori di serving moderni combinano il continuous batching con il Chunked Prefill (o Piggybacking):
- Il prompt lungo di una nuova richiesta viene spezzato in blocchi (chunk da es. 512 token).
- Nello stesso batch di calcolo, la GPU esegue simultaneamente il Prefill di un chunk di una nuova richiesta e il Decoding di un singolo token per 50 richieste già attive.
- Questo approccio satura sia la banda passante della memoria sia le ALU/Tensor Core, evitando picchi di latenza (latency spikes) per le richieste in fase di generazione.
Confronto di Efficienza
| Parametro | Static Batching | Continuous Batching |
|---|---|---|
| Unità di Scheduling | Intera sequenza (dall’inizio alla fine) | Singolo step di decoding (1 token) |
| Utilizzo della GPU | Basso (frammentato da padding e thread inattivi) | Saturazione quasi costante (90–98%) |
| Time-to-First-Token (TTFT) | Alto (bloccato in coda dal batch precedente) | Minimizzato (inserimento immediato) |
| Throughput Globale | Lineare rispetto al carico | Incremento tipico di 3× – 8× (token/sec) |
Abbinato a PagedAttention per la gestione dinamica dei blocchi di memoria, il Continuous Batching costituisce l’infrastruttura fondamentale che permette ai moderni cluster di inferenza (sia su cloud commerciali che in deployment on-premise) di servire centinaia di richieste concorrenti con latenza uniforme.
Come funziona lo Speculative Decoding e in che modo riduce la latenza di generazione nei modelli linguistici?
Lo Speculative Decoding (o Speculative Sampling) è una tecnica algoritmica progettata per abbattere la latenza di generazione (Time-per-Output-Token) nei Large Language Model senza alterare minimamente la distribuzione di probabilità dell’output.
Il principio cardine si basa su una proprietà fondamentale dell’hardware moderno: verificare K token in parallelo richiede all’incirca lo stesso tempo che generarne uno solo in modo autoregressivo.
Il Problema: Il Limite della Banda di Memoria (Memory-Bound Decoding)
Durante la generazione ordinaria, per produrre un singolo token un modello da 70 miliardi di parametri deve caricare decine di gigabyte di pesi dalla memoria HBM/VRAM ai registri di calcolo della GPU.
Poiché le GPU moderne hanno un’enorme potenza di calcolo parallelo (TFLOPS) ma una banda passante finita (GB/s), la generazione sequenziale token-per-token lascia le unità di calcolo perennemente inattive (memory bandwidth bottleneck).
I Due Attori dello Speculative Decoding
- Target Model (Mlarge): Il modello principale ad alte prestazioni (es. Llama-3-70B, GPT-4, Gemini), computazionalmente pesante e lento nel decoding token-per-token.
- Draft Model (Msmall): Un modello molto più compatto e rapido (es. Llama-3-8B o un modello da 1B parametri addestrato sullo stesso tokenizer/distribuzione), che genera token a una frazione del costo temporale.
(Nota: esistono anche varianti come Medusa o EAGLE che utilizzano testine di predizione multi-token aggiuntive innestate direttamente sul modello target, senza richiedere un secondo modello separato).
Il Meccanismo in 3 Fasi
Ad ogni iterazione, il sistema esegue un ciclo composto da:
[Draft Model: Genera K token veloci]
│ (es. "il", "gatto", "salta", "sul")
▼
[Target Model: Singolo forward-pass parallelo su K+1 posizioni]
│
▼
[Criterio di Accettazione / Rifiuto Modificato]
│
├─ Accetta: "il", "gatto", "salta"
└─ Rifiuta: "sul" ──> Corregge con il token proprio del Target: "sul tavolo"
1. Fase di Proposta (Draft Phase) Il modello piccolo genera in modo autoregressivo una breve sequenza di K token ipotetici (tipicamente K=3÷6). Poiché il modello piccolo ha una dimensione ridotta, genera questi K token in una frazione del tempo impiegato dal modello target.
2. Fase di Verifica Parallela (Verification Phase) Il modello target riceve l’intera sequenza di K token e ne calcola i logit in un singolo forward pass parallelo (operazione di Prefill, che è tipicamente compute-bound e satura i Tensor Core senza penalità di latenza).
3. Fase di Accettazione / Rifiuto (Speculative Rejection Sampling) Il sistema confronta la distribuzione di probabilità del draft model q(x) con quella del target model p(x):
- Se p(x)≥q(x), il token viene sempre accettato.
- Se p(x)<q(x), il token viene accettato con probabilità pari al rapporto q(x)p(x).
- Se un token viene rifiutato, la catena speculativa si arresta lì; il modello target campiona un token correttivo dalla distribuzione residua:pres(x)=∑ymax(0,p(y)−q(y))max(0,p(x)−q(x))
Grazie a questa correzione probabilistica, l’output finale è matematicamente identico a quello che avrebbe prodotto il modello target operando da solo.
Guadagno Prestazionale ed Efficienza
Se il draft model indovina N token su K con un alto tasso di accettazione α (solitamente tra il 60% e l’85% per testi ordinari e codice):
- In un singolo ciclo temporale equivalente a 1 forward pass del modello target (più il piccolo overhead del draft), vengono emessi N+1 token.
- Il fattore di accelerazione (speedup) medio si attesta tipicamente tra 1.8× e 3.5× nella latenza di generazione.
| Aspetto | Decoding Standard Autoregressivo | Speculative Decoding |
|---|---|---|
| Token per forward pass del Target | Esattamente 1 | Da 1 fino a K+1 (mediamente 2–4) |
| Garanzia di Qualità | Baseline | 100% Identica (lossless) |
| Regime Computazionale | Memory-bound costante | Sfrutta i cicli GPU liberi nel forward pass |
| Overhead | Nessuno | Memoria VRAM aggiuntiva per il Draft Model / Teste aggiuntive |
Lo Speculative Decoding è particolarmente efficace in scenari a basso batch size (singolo utente o interazione in tempo reale), dove la GPU dispone di ampie risorse di calcolo inutilizzate per singolo step di decoding.
Come funzionano le tecniche di Speculative Decoding ‘senza modello ausiliario’ come Medusa ed EAGLE?
Le tecniche come Medusa ed EAGLE eliminano la necessità di mantenere un secondo modello separato (draft model), superando i due problemi classici dello Speculative Decoding tradizionale:
- Overhead di memoria e gestione: Non serve caricare in VRAM un modello secondario né allineare tokenizer differenti.
- Disallineamento semantico: Un modello draft compatto fatica a predire la distribuzione esatta di un modello molto più grande (es. un 1B parametro contro un 70B).
Entrambi i metodi innestano moduli leggeri direttamente sull’architettura del modello target, ma con due filosofie distinte per gestire il contesto futuro: teste parallele indipendenti (Medusa) vs auto-regressione a livello di feature latenti (EAGLE).
1. Medusa: Teste di Predizione Multi-Token e Tree-Attention
Medusa aggiunge K teste di decodifica (Medusa Heads — semplici strati lineari o MLP a singolo layer) direttamente sopra l’ultimo strato di rappresentazione nascosta (hidden state) del modello base congelato.
- Predizione parallela simultanea: Se il modello target genera lo stato per il token al passo t, la testa 1 predice il token t+1, la testa 2 predice t+2, la testa 3 predice t+3, e così via, in un unico passaggio in avanti.
- Costruzione dell’albero di candidati (Tree Drafting): Poiché le teste predicono in modo indipendente senza condizionarsi a vicenda, Medusa genera per ciascuna testa i top-P candidati più probabili e li combina formando un albero di sequenze candidate (es. fino a decine di rami plausibili).
- Tree Attention Verification: Il modello target verifica tutti i rami dell’albero simultaneamente in un singolo forward pass utilizzando una speciale maschera di attenzione ad albero (Tree Attention), che impedisce ai rami diversi di vedersi reciprocamente. Viene accettato il prefisso valido più lungo.
2. EAGLE (Extrapolation Algorithm for Greater Language-model Efficiency)
Medusa soffre di un limite intrinseco: predire t+2 o t+3 basandosi solo sullo stato nascosto al tempo t ignora i token intermedi, portando a tassi di accettazione che degradano rapidamente per le posizioni più lontane.
EAGLE risolve questo problema spostando l’auto-regressione dallo spazio dei token allo spazio delle feature (vettori nascosti):
- Architettura leggera (EAGLE Head): Un singolo layer Transformer compatto (un solo blocco di attenzione + feed-forward) aggiunto sopra il modello target.
- Auto-regressione sulle feature: Per predire il token al passo t+1, EAGLE concatena l’embedding del token ipotizzato al passo t con lo stato nascosto (feature vector) del modello target.
- Perché funziona meglio: La sequenza di stati nascosti di un grande LLM contiene una densità di informazione e una stabilità semantica molto superiore rispetto ai semplici token discreti. L’auto-regressione nello spazio delle feature permette alla singola testa leggera di simulare fedelmente il comportamento dell’intero modello profondo, garantendo un tasso di accettazione (acceptance rate) notevolmente più alto rispetto a Medusa.
- Verifica: Anche EAGLE struttura i candidati in un albero (Tree Drafting) e usa la Tree Attention per convalidare il percorso corretto in un unico passaggio del modello principale.
Confronto Architetturale
| Caratteristica | Speculative Standard (es. Llama-70B + 8B) | Medusa | EAGLE / EAGLE-2 |
|---|---|---|---|
| Modello Ausiliario | Modello Transformer completo separato | Nessuno (K teste lineari/MLP) | Nessuno (1 singolo strato Transformer) |
| Overhead Parametri | 10% – 25% della memoria base | < 1% dei parametri totali | < 1% dei parametri totali |
| Generazione Draft | Auto-regressiva su token | Parallela non-autoregressiva | Auto-regressiva su feature/embedding |
| Topologia di Verifica | Catena lineare (K token in fila) | Albero di candidati (Tree Attention) | Albero dinamico contestuale (Tree Attention) |
| Speedup Tipico | ~1.8× – 2.2× | ~1.9× – 2.5× | ~2.5× – 3.5× |
Questi approcci rappresentano lo stato dell’arte per l’accelerazione dell’inferenza nei motori ad alte prestazioni, poiché offrono la massima riduzione di latenza possibile senza il costo computazionale e di allocazione VRAM di un secondo LLM.
Come funziona la maschera di Tree Attention utilizzata in Medusa ed EAGLE per verificare i candidati in parallelo?
La Tree Attention risolve il problema di verificare contemporaneamente più sequenze candidate ramificate senza dover eseguire multipli forward pass indipendenti del modello target.
Invece di linearizzare i candidati in sequenze separate, l’intero albero di ipotesi viene appiattito in un unico batch e verificato in un singolo passaggio grazie a una maschera di attenzione 2D non-causale custom.
1. La Struttura dell’Albero di Candidati (Tree Topology)
Supponiamo che il modulo draft (Medusa o EAGLE) parta dal token accettato T0 e proponga i candidati strutturati nel seguente albero a 6 nodi:
[T0] (Radice - contesto base)
/ \
[A1] [B1]
/ \ \
[A2] [A3] [B2]
I percorsi candidati generati sono 3:
- T0→A1→A2
- T0→A1→A3
- T0→B1→B2
2. Appiattimento e Maschera di Attenzione Custom
I nodi dell’albero vengono appiattiti in una sequenza 1D di token: [T0,A1,B1,A2,A3,B2] (indici da 0 a 5).
La regola fondamentale dell’attenzione ad albero è: un token X può prestare attenzione a un token Y se e solo se Y è un antenato di X (o X stesso) lungo il cammino che porta alla radice.
La matrice della maschera di attenzione M∈{0,1}6×6 assume la seguente configurazione (dove 1 indica attenzione consentita e 0 indica mascheramento a −∞ prima della Softmax):
\begin{array}{c\|cccccc} \text{Query} \backslash \text{Key} & T_0 & A_1 & B_1 & A_2 & A_3 & B_2 \\ \hline T_0 & 1 & 0 & 0 & 0 & 0 & 0 \\ A_1 & 1 & 1 & 0 & 0 & 0 & 0 \\ B_1 & 1 & 0 & 1 & 0 & 0 & 0 \\ A_2 & 1 & 1 & 0 & 1 & 0 & 0 \\ A_3 & 1 & 1 & 0 & 0 & 1 & 0 \\ B_2 & 1 & 0 & 1 & 0 & 0 & 1 \\ \end{array}
- Esempio A3 (riga 4): Vede se stesso (A3), il genitore (A1) e la radice (T0). Non vede il fratello (A2) né i rami dell’altro sotto-albero (B1,B2).
- Esempio B2 (riga 5): Vede se stesso (B2), il genitore (B1) e la radice (T0).
3. Gestione del Positional Encoding (RoPE 1D)
Oltre alla maschera di attenzione, un modello Transformer necessita delle informazioni posizionali (solitamente RoPE – Rotary Position Embedding).
Nell’attenzione standard lineare, i token avrebbero posizioni sequenziali 0,1,2,3,4,5. In una struttura ad albero, la posizione di ciascun token corrisponde alla sua profondità nell’albero rispetto al contesto originario:
| Token | Indice Appiattito | Genitore | Profondità Relativa (Offset RoPE) |
|---|---|---|---|
| T0 | 0 | – | p+0 |
| A1 | 1 | T0 | p+1 |
| B1 | 2 | T0 | p+1 |
| A2 | 3 | A1 | p+2 |
| A3 | 4 | A1 | p+2 |
| B2 | 5 | B1 | p+2 |
In questo modo, sia A1 che B1 ricevono lo stesso identico indice posizionale p+1, e A2,A3,B2 ricevono la posizione p+2.
4. Implementazione Hardware nei Kernel GPU (FlashAttention)
Nei motori moderni (vLLM, SGLang, TensorRT-LLM):
- Integrazione con FlashAttention: La matrice di maschera densa N×N non viene memorizzata esplicitamente in VRAM. I kernel personalizzati ricevono direttamente un piccolo array con la topologia dell’albero (parent pointers e depth offsets).
- Calcolo del Softmax: Durante il calcolo dei blocchi QKT, il kernel verifica al volo se l’indice della chiave appartiene al set di antenati dell’indice della query; in caso contrario, applica il bitmasking impostando il punteggio a −∞.
- Aggiornamento KV-Cache: Una volta completato il passaggio in avanti, il sistema individua il cammino accettato più lungo (es. T0→A1→A3), salva solo i vettori K,V di quel percorso nella KV-Cache permanente e scarta tutti i blocchi relativi ai rami non validi.
Questo schema permette di verificare fino a 64 candidati distribuiti su alberi a profondità 4–5 all’interno dello stesso ciclo hardware richiesto per verificare un singolo token lineare.
Come funziona l’algoritmo di Tree Drafting dinamico introdotto in EAGLE-2 rispetto agli alberi statici?
EAGLE-2 risolve il limite architetturale principale di Medusa e del primo EAGLE: l’utilizzo di una struttura dell’albero statica (fissa a priori) per ogni passo di generazione.
In un albero statico, il numero di nodi, i rami e la profondità sono prefissati (es. un albero rigido a 64 nodi). Questo approccio è inefficiente perché:
- Nei passaggi a bassa entropia (es. codice o frasi standard, dove il modello è sicuro al 99%), l’albero spreca nodi esplorando rami alternativi inutili anziché andare in profondità lungo l’unica catena corretta.
- Nei passaggi ad alta entropia (bivi logici o scelte creative con alta incertezza), l’albero statico alloca troppi nodi in profondità su percorsi speculativi che verranno quasi certamente rigettati subito.
Il Meccanismo di EAGLE-2: Context-Aware Dynamic Tree
EAGLE-2 introduce un algoritmo di espansione dell’albero guidato dal tasso di accettazione stimato (Confidence Score) in tempo reale:
1. Punteggio di Confidenza Cumulativo Durante la fase di draft, per ogni token candidato xi generato al livello d, il modello leggero calcola la sua probabilità condizionata P(xi∣x<i). Il punteggio di confidenza del percorso P fino a quel nodo viene calcolato come prodotto cumulativo delle probabilità lungo il ramo:
Score(P)=k=1∏dP(xk∣x<k)
2. Espansione Greedy ad Alta Confidenza (Best-First Search) Invece di espandere tutti i nodi in modo uniforme secondo uno schema rigido:
- Il sistema mantiene una coda di priorità con tutti i nodi foglia candidati.
- A ogni passo di drafting, espande soltanto i nodi con il punteggio cumulativo più elevato, finché non viene raggiunto il budget massimo di token per l’albero (es. 32 o 64 nodi).
- Adattamento di forma:
- Bassa entropia: L’albero diventa una catena profonda e stretta (es. profondità 7–8 con 1 solo ramo principale).
- Alta entropia: L’albero si allarga orizzontalmente a ventaglio a bassa profondità (es. profondità 2–3 con molte ramificazioni brevi).
Contesto Semplice (Bassa Entropia): Contesto Complesso (Alta Entropia):
[T0] [T0]
│ / | \
[A1] [A1][B1][C1]
│ / \ |
[A2] [A2][A3][B2]
│
[A3] (Catena profonda) (Albero largo e piatto)
3. Riposizionamento e Generazione della Maschera Dinamica
Poiché la topologia dell’albero cambia forma a ogni singolo step temporale di inferenza:
- Generazione dinamica della Tree Mask: Il runtime genera al volo la matrice di maschera 2D e il vettore dei RoPE position offsets in base alla specifica geometria appena calcolata.
- Kernel Custom ad Hoc: Il kernel CUDA riceve il nuovo set di parent pointers generato dalla coda di priorità senza overhead significativo di sincronizzazione CPU-GPU.
Confronto Prestazionale
| Metrica | EAGLE (Albero Statico) | EAGLE-2 (Albero Dinamico) |
|---|---|---|
| Topologia Albero | Rigida e invariante per ogni token | Dinamica, modulata dall’entropia locale |
| Token Accettati per Step (Mean) | ~2.3 – 3.0 token | ~3.2 – 4.2 token |
| Spreco di Nodi Draft | Alto (rami inutili in contesti a bassa entropia) | Minimizzato (i nodi vanno dove serve) |
| Overhead di Drafting | Basso | Trascurabile (gestione della coda in SRAM/GPU) |
| Speedup Complessivo su LLM | ~2.5× | ~3.0× – 4.0× |
Grazie a questa flessibilità dinamica, EAGLE-2 massimizza la lunghezza media dei prefissi convalidati a ogni passaggio del modello principale, portando lo speedup dell’inferenza vicina al limite teorico consentito dalla distribuzione del modello target.