CPU con istruzioni SIMD
Sfruttamento nativo di AVX2 e AVX-512 su x86_64, e di ARM NEON/SVE su Apple Silicon e sistemi edge.
Non una scatola nera: un motore che controlliamo.
Il nostro motore d'inferenza locale, scritto in C++17 con ABI C e wrapper .NET e Python: controllo totale dell'intera catena di esecuzione, dal file del modello al token.
In un panorama dominato da soluzioni "chiavi in mano" e framework preconfezionati, la maggior parte delle aziende si limita ad agire da mero consumatore di API. L'illusione di velocità iniziale si paga nel lungo periodo.
Per noi l'IA non può essere una scatola nera senza leve di ottimizzazione. Per questo abbiamo scelto la strada più complessa, ma l'unica sostenibile per il futuro enterprise: progettare e sviluppare il nostro motore d'inferenza locale, DesireeIA.
Nasce da una precisa esigenza strategica e ingegneristica: il controllo totale dell'intera catena di esecuzione.
Sfruttamento nativo di AVX2 e AVX-512 su x86_64, e di ARM NEON/SVE su Apple Silicon e sistemi edge.
Accelerazione diretta tramite API moderne (CUDA, Vulkan, Metal): schede enterprise, server cloud o dispositivi periferici a basso consumo.
Dati scambiati ridotti al minimo con serializzazione binaria ottimizzata, per architetture distribuite e edge-computing.
I framework in linguaggi con gestione automatica della memoria introducono pause imprevedibili durante l'inferenza e picchi di latenza incoerenti (jitter). Con il core in C++ nativo l'allocazione è deterministica: zero pause impreviste e un profilo di latenza costante.
Il runtime che carica in memoria i parametri di un modello (pesi), riceve una sequenza di input (prompt) ed esegue le operazioni di algebra lineare necessarie a generare l'output token per token.
il token generato rientra come input del passo successivo
Variabili matematiche apprese durante l'addestramento: la "conoscenza" memorizzata nelle matrici di attenzione e nei layer feed-forward. In FP16 ogni peso occupa 2 byte. La quantizzazione (GGUF in INT8, INT4 o K-quant) lo comprime a 8, 4 o persino 2 bit: meno memoria, più velocità sul bus.
Esempio: Llama 3.1 8B (8,03 miliardi di parametri); bit effettivi per peso, scale incluse.
GGUF è un contenitore binario che memorizza in un unico file sia i metadati dell'architettura (numero di layer, teste d'attenzione, dimensione del contesto) sia i pesi in blocchi quantizzati e allineati.
Una rigorosa stratificazione per coniugare le prestazioni del codice nativo con la flessibilità dei linguaggi ad alto livello.
Il motore matematico ed esecutivo: memoria, caricamento dei GGUF, calcolo tensoriale, vettorizzazione SIMD e controllo hardware diretto.
Un'interfaccia C pura (extern "C") elimina il name mangling del C++ e garantisce stabilità binaria tra compilatori e runtime, con interoperabilità a zero overhead.
API orientate agli oggetti, asincrone e idiomatiche sopra l'ABI C, senza sacrificare la velocità di esecuzione sottostante.
Uno degli aspetti più critici di un motore locale è la ripartizione strategica delle risorse tra disco, RAM di sistema e VRAM della scheda video.
Invece di leggere il GGUF e copiarlo sequenzialmente in RAM con I/O tradizionale (spreco di memoria e di tempo), il motore usa mmap: il sistema operativo mappa i blocchi del file nello spazio di memoria virtuale e li legge zero-copy solo quando servono. L'avvio è quasi istantaneo.
BLa VRAM offre una banda di memoria enormemente superiore a quella della RAM di sistema. Il motore può dividere i layer: con 8 GB di VRAM e un modello da 12 GB, ne carica ad esempio 20 sulla GPU e lascia gli altri 12 sulla CPU.
La CPU elabora i primi layer, passa lo stato intermedio alla VRAM via PCIe e la GPU completa il forward pass. Modello da 12 GB su 32 layer = 0,375 GB per layer.
Per capire perché le prestazioni cambiano durante l'esecuzione servono due concetti: la finestra di contesto e la KV Cache.
La lunghezza massima della sequenza (prompt + risposte precedenti) che il modello elabora in un singolo passo. Poiché la Self-Attention mette in relazione ogni token con tutti i precedenti, la complessità teorica è quadratica:
N = lunghezza del contesto.
Per non ricalcolare l'attenzione di tutti i token passati a ogni passo, i vettori Key e Value di ogni layer vengono salvati in memoria: per il token N+1 il motore calcola K e V solo del nuovo token e recupera gli altri dalla cache. Il costo in memoria cresce linearmente col contesto:
Llama 3.1 8B: L = 32, h_kv = 8, d = 128, b = 2 byte (FP16) → 128 KiB / token
Oltre circa 37.141 token la KV Cache supera la dimensione dei pesi stessi.
Lettura del prompt: centinaia o migliaia di token elaborati in parallelo (moltiplicazione matrice-matrice, GEMM). Servono enormi quantità di operazioni in virgola mobile: il limite è la potenza di calcolo. Determina il Time To First Token (TTFT).
Un token alla volta (moltiplicazione matrice-vettore, GEMV): per ogni token il motore legge l'intera matrice dei pesi, gigabyte di dati, dalla VRAM/RAM. I core aspettano i dati dal bus. Determina la velocità continua, in Tokens Per Second (TPS).
Token al secondo teorici per Llama 3.1 8B in Q4_K_M: è un limite fisico di banda, non una misura. Più il contesto cresce, più la KV Cache aumenta i dati da leggere per token e i TPS scendono, mentre l'occupazione di VRAM/RAM sale.
Dall'allocazione della KV Cache alla gestione millimetrica degli stream di calcolo: prestazioni di livello enterprise, sicurezza dei dati ed efficienza.
Parla con la community
Vedi chi è online, scrivi messaggi privati e scambia immagini e file (max 6 MB). Serve un account gratuito.
Accedi Crea un account