Un’unità SSD NVMe di recente fattura può dichiarare diversi milioni di IOPS, ovvero milioni di operazioni di input/output al secondo. Sono valori che paiono quasi incompatibili con l’idea tradizionale di un dispositivo di memorizzazione: il sistema operativo chiede un blocco di dati, il supporto di memorizzazione lo recupera, restituisce il risultato e soltanto dopo passa all’operazione successiva. Ma un SSD NVMe non lavora affatto in questo modo.
La vera differenza rispetto alle vecchie interfacce di storage non consiste soltanto nella maggiore larghezza di banda offerta da PCI Express (PCIe). NVMe nasce per consentire al sistema operativo di mantenere un grande numero di operazioni contemporaneamente “in volo”, distribuendole fra CPU, controller SSD, canali NAND e chip di memoria.
Le abilità di NVMe in termini di parallelizzazione
Questa capacità di lavorare su molte richieste contemporaneamente emerge chiaramente nei benchmark come CrystalDiskMark, fio e simili. Nei test non conta soltanto quanti dati l’SSD riesce a trasferire, ma anche quante operazioni possono restare in attesa di essere elaborate nello stesso momento.
Il parametro queue depth indica proprio il numero di richieste I/O che il sistema mantiene contemporaneamente in coda verso l’SSD. Con una queue depth pari a 1, il dispositivo riceve una richiesta, la completa e poi passa alla successiva; con una queue depth pari a 32, invece, può avere fino a 32 operazioni già disponibili da distribuire tra le proprie risorse interne.
Per questo un SSD può raggiungere valori superiori al milione di IOPS nei test con molte richieste da smaltire, ma ottenere risultati molto più bassi quando il carico consiste in una sola operazione alla volta. Con una coda quasi vuota non può sfruttare fino in fondo il parallelismo del controller e dei chip NAND.

Il limite storico non era soltanto il disco meccanico
Per capire NVMe conviene partire da AHCI, il modello tradizionalmente utilizzato per le unità SATA.
AHCI nacque quando il riferimento erano gli hard disk magnetici: una richiesta di lettura poteva richiedere lo spostamento fisico delle testine e l’attesa della rotazione del piatto; aveva quindi senso mantenere una coda relativamente limitata di operazioni che il dispositivo potesse riordinare. Con SATA e AHCI il meccanismo Native Command Queuing, o NCQ, permette tipicamente di avere fino a 32 comandi pendenti.
Per un hard disk era già molto: il firmware poteva esaminare le richieste ricevute e scegliere un execution order più conveniente, riducendo gli spostamenti delle testine.
Un SSD cambia completamente il piano di gioco: non esiste una testina da spostare e, soprattutto, il dispositivo contiene numerosi elementi capaci di lavorare in parallelo. Continuare a utilizzare un modello progettato intorno alle necessità dei dischi magnetici avrebbe significato lasciare inutilizzata una parte consistente delle capacità dell’hardware. NVMe nasce precisamente per rimuovere tale collo di bottiglia.
NVMe non utilizza una sola coda
L’elemento centrale dell’architettura NVMe e’ il concetto di Submission Queue e Completion Queue.
La Submission Queue contiene i comandi che l’host, quindi tipicamente il sistema operativo attraverso il driver NVMe, vuole sottoporre all’SSD. La Completion Queue svolge il percorso inverso: il controller vi deposita le informazioni relative ai comandi completati.
Le code risiedono normalmente nella memoria del sistema e assumono la forma di buffer circolari. Un comando NVMe standard occupa 64 byte; una voce della Completion Queue comunica invece al software host quale operazione è terminata e con quale risultato. La specifica NVMe identifica un comando completato attraverso la combinazione tra identificatore della Submission Queue e Command Identifier.
Il punto fondamentale, però, è un altro: NVMe consente di creare molte code I/O indipendenti. I campi utilizzati dal protocollo per identificare Submission Queue e Completion Queue sono a 16 bit e l’architettura prevede fino a 65.535 I/O Submission Queue e altrettante I/O Completion Queue, anche se un SSD reale e il sistema operativo ne utilizzano molte meno. Una singola coda può inoltre raggiungere una profondità massima teorica di 65.536 voci, entro i limiti dichiarati dal controller.
Sono valori enormemente superiori a quelli necessari per un normale PC desktop: la specifica NVMe, tuttavia, vuole eliminare il collo di bottiglia strutturale costituito da una singola coda condivisa.
Dalle code NVMe al parallelismo della memoria NAND
NVMe permette al sistema operativo di distribuire le richieste su più code, evitando che molti core della CPU debbano contendersi continuamente una singola struttura condivisa. Non esiste necessariamente una corrispondenza rigida “un core = una coda“, ma l’architettura è pensata proprio per gestire in modo efficiente molti flussi I/O contemporanei.
Una queue depth pari a 32, però, non significa che l’unità SSD esegua nello stesso istante 32 accessi alla memoria NAND. Significa che il controller ha fino a 32 richieste disponibili da organizzare e distribuire.
Grazie al parallelismo interno dell’SSD, il controller comunica con più canali NAND e, a seconda del modello, con diversi package e die di memoria. Se le richieste riguardano risorse che possono essere utilizzate in parallelo, alcune letture o scritture possono procedere contemporaneamente mentre altre restano in attesa.
Una coda più profonda offre quindi al controller più lavoro tra cui scegliere e maggiori possibilità di tenere occupate le diverse unità interne. È uno dei motivi per cui le prestazioni in IOPS crescono sensibilmente quando aumenta il numero di richieste da smaltire.
IOPS non significa MB/s
Un equivoco frequente consiste nell’equiparare IOPS e velocità di trasferimento: sono grandezze correlate, ma descrivono aspetti diversi.
Gli IOPS indicano quante operazioni di input/output il dispositivo completa ogni secondo; il throughput indica invece quanti byte vengono trasferiti. La relazione semplificata è la seguente:
throughput = IOPS x dimensione I/O
Un milione di letture da 4 KB corrispondono teoricamente a circa 1.000.000 x 4 KB ≃ 3,8 GB/s
Un milione di letture da 128 KB richiederebbero invece una larghezza di banda enormemente superiore e incontrerebbero immediatamente il limite dell’interfaccia PCIe o del controller.
È per questo motivo che i produttori riportano separatamente valori come i seguenti, proprio perché misurano caratteristiche differenti del dispositivo:
- lettura sequenziale in GB/s;
- scrittura sequenziale in GB/s;
- IOPS casuali 4K.
Come viaggiano comandi e dati tra CPU e SSD
Le code NVMe risiedono nella memoria di sistema, ma il controller deve sapere quando sono disponibili nuovi comandi. Il driver aggiorna per questo appositi registri doorbell: dopo aver inserito una o più richieste nella Submission Queue, modifica il relativo Submission Queue Tail Doorbell, segnalando all’SSD fino a quale posizione della coda può trovare nuovi comandi.
Il controller li preleva e usa il DMA, Direct Memory Access, per trasferire i dati direttamente tra SSD e RAM attraverso PCIe, senza costringere la CPU a copiarli byte per byte.
Terminata l’operazione, il controller inserisce una voce nella Completion Queue. Il sistema può ricevere la notifica tramite interrupt (MSI/MSI-X) oppure, nei carichi ad alte prestazioni, ricorrere anche al polling. NVMe supporta inoltre l’interrupt coalescing, che permette di raggruppare più completamenti prima di interrompere la CPU: si riduce così l’overhead, al prezzo di un possibile piccolo aumento della latenza.
Submission Queue, doorbell, DMA e Completion Queue formano un meccanismo pensato per mantenere molte operazioni in movimento con il minimo intervento possibile della CPU.

Perché gli SSD con più memorie NAND possono essere più veloci
Il parallelismo aiuta anche a capire una caratteristica apparentemente anomala delle famiglie di SSD. Due modelli appartenenti alla stessa serie, per esempio da 500 GB e 2 TB, possono avere prestazioni differenti pur utilizzando lo stesso controller e la stessa interfaccia PCIe. Uno dei motivi è proprio il numero di risorse NAND disponibili.
Una capacità maggiore può significare più die NAND e quindi più opportunità per distribuire letture e scritture contemporaneamente.
Non è una regola universale: dipende dall’organizzazione dei package, dalla generazione NAND, dal controller e dalla configurazione scelta dal produttore. Spiega però perché la sola dicitura “PCIe 4.0 x4” o “PCIe 5.0 x4” non dice quasi nulla sulle performance reali dell’unità.
Molte operazioni quotidiane lavorano con queue depth molto basse. Avvio di programmi, caricamento di DLL, lettura di file di configurazione, accesso a piccoli database locali e numerose operazioni svolte durante l’avvio di Windows generano sequenze molto più frammentate di quanto suggeriscano i benchmark estremi.
In queste condizioni contano soprattutto la latenza a QD1, cioè quanto rapidamente l’SSD completa una singola richiesta quando non ha altre operazioni da elaborare, e le prestazioni random 4K a bassa queue depth, molto più vicine ai piccoli accessi sparsi tipici di sistema operativo e applicazioni rispetto ai benchmark con code profonde.
Pesano inoltre l’efficienza del controller e del firmware, che devono gestire mapping, priorità, garbage collection e richieste concorrenti senza introdurre ritardi. Anche la cache SLC ha un ruolo importante: finché è disponibile può accelerare sensibilmente le scritture, ma una volta esaurita emergono le prestazioni reali della NAND TLC o QLC sottostante, spesso molto inferiori.
Un SSD che raggiunge 2 milioni di IOPS con centinaia di richieste contemporanee non sarà necessariamente due volte più reattivo sul desktop di uno che arriva a un milione.
PCIe rende possibile il parallelismo
Si tende spesso a riassumere l’evoluzione degli SSD in questo modo:
SATA ⇒ 600 MB/s PCIe 3.0 ⇒ circa 4 GB/s PCIe 4.0 ⇒ circa 8 GB/s PCIe 5.0 ⇒ circa 16 GB/s
Sono valori teorici utili per orientarsi, ma raccontano soltanto una parte della storia. PCIe fornisce un collegamento molto più veloce e diretto rispetto a SATA; NVMe definisce invece come sfruttarlo efficacemente per lo storage dei dati.
Se collegassimo una memoria flash velocissima con un protocollo incapace di mantenere abbastanza richieste in attesa o richieste già accodate, non riusciremmo comunque a sfruttarne tutto il parallelismo.
Il salto SATA-NVMe è quindi contemporaneamente fisico e logico: PCIe aumenta enormemente la capacità del collegamento; NVMe permette di riempirlo con moltissime operazioni indipendenti.
Un piccolo esperimento con CrystalDiskMark
Valutazioni interessanti e molto pertinenti si possono fare anche senza attrezzarsi con strumenti professionali. In CrystalDiskMark, anziché guardare soltanto il primo valore di lettura sequenziale, è molto più istruttivo confrontare i test random 4K eseguiti con differenti queue depth.

Due risultati tipici possono essere indicati come:
- RND4K Q32T1
- RND4K Q1T1
La nomenclatura può cambiare tra le versioni, ma il concetto rimane: Q32 significa una queue depth fino a 32 richieste; Q1 significa essenzialmente una richiesta in volo; T1 indica un singolo thread utilizzato dal benchmark. Il divario tra Q1 e Q32 offre una rappresentazione concreta della capacità del dispositivo di sfruttare il parallelismo.
Si può osservare anche la latenza: aumentando la queue depth cresce spesso enormemente il throughput complessivo, mentre il tempo trascorso da una singola richiesta nella coda può aumentare: sono due misure diverse.
In generale, le prestazioni dello storage dipendono anche dal software: file system, scheduler I/O, driver, motore database e applicazione devono essere capaci di produrre un numero sufficiente di operazioni asincrone.
NVMe è soprattutto un protocollo progettato per non aspettare
Ridurre NVMe alla frase “SSD collegato via PCIe” fa perdere la caratteristica tecnicamente più interessante dello standard, che avevamo evidenziato anche a suo tempo mettendo a confronto hard disk, unità SATA e PCIe NVMe.
NVMe nasce per evitare che CPU e memoria flash trascorrano buona parte del tempo ad aspettarsi reciprocamente.
Le Submission Queue permettono alla CPU di consegnare numerosi attività da svolgere al controller; le Completion Queue consentono di recuperarne efficientemente i risultati. Più code riducono la contesa tra core. DMA trasferisce i dati direttamente tra dispositivo e RAM; il controller distribuisce le operazioni tra canali e die NAND e può mantenere contemporaneamente numerosi comandi in differenti fasi di esecuzione. Il risultato è un modello profondamente asincrono.
Parlare soltanto dei 7, 12 o 14 GB/s dichiarati racconta poco di come funziona realmente un SSD NVMe: la caratteristica più importante di questi prodotti non è infatti soltanto quanto velocemente riescano a trasferire un singolo flusso di dati, ma quante operazioni tengono contemporaneamente in movimento senza trasformare la coda I/O nel vero collo di bottiglia.