Windows: perché copiare migliaia di piccoli file è molto più lento di uno grande

Copiare 100 GB raccolti in pochi file può essere molto più veloce rispetto al trasferimento della stessa quantità di dati suddivisa in centinaia di migliaia di elementi. Spieghiamo il motivo e come intervenire.

Un’unità SSD capace di scrivere diversi gigabyte al secondo può sorprendere per la sua velocità quando deve copiare un file da 100 GB e, pochi minuti dopo, sembrare improvvisamente lento trasferendo una cartella della stessa dimensione composta da centinaia di migliaia di file. Il volume complessivo dei dati non è cambiato: sono sempre 100 GB. Eppure il tempo necessario può aumentare enormemente.

Trasferire i dati è soltanto una parte del lavoro: sistema operativo e file system devono compiere una serie di operazioni indipendenti dalla dimensione di ciascun file. Sono attività che con un unico file da 100 GB si verificano sostanzialmente una volta. Con 500.000 file da 200 KB devono ripetersi mezzo milione di volte. Ecco quindi che la velocità di trasferimento, misurata in MB/s o GB/s, smette di raccontare tutta la storia.

Un file non è soltanto una sequenza di byte

Quando Windows copia un file non prende semplicemente un blocco di dati dalla sorgente per riversarlo nella destinazione. Deve innanzitutto individuare il file nella directory, aprirlo, verificarne gli attributi e ottenere le informazioni necessarie per accedervi. Sul dispositivo di destinazione deve controllare il percorso, creare un nuovo oggetto file, assegnargli spazio, aggiornare le strutture del file system, trasferire i dati, impostare attributi e timestamp e infine chiudere il file.

Prendiamo NTFS, il file system normalmente utilizzato sulle unità Windows. Al centro del suo funzionamento vi è Master File Table (MFT): ogni file presente sul volume dispone di almeno una voce nella MFT, contenente o collegata alle informazioni necessarie per descriverlo. Si tratta dei cosiddetti metadati ovvero informazioni su dimensioni, attributi, timestamp, autorizzazioni e posizione dei dati.

Creare 500.000 file significa quindi creare e modificare una quantità enorme di metadati. Il dispositivo non deve gestire soltanto 100 GB: deve costruire 500.000 oggetti distinti nel file system.

Per ciascuno deve inoltre gestire il nome, la directory di appartenenza, le informazioni temporali, gli attributi e l’allocazione dello spazio. A seconda delle modalità di copia, NTFS può dover trasferire anche permessi e ACL, informazioni sul proprietario, dati di auditing e Alternate Data Streams associati ai file.

Allocare spazio 500.000 volte non equivale ad allocarlo una volta

Anche l’allocazione dello spazio sul dispositivo comporta lavoro: il file system deve stabilire quali cluster risultano disponibili, assegnarli al nuovo file e aggiornare le strutture utilizzate per indicare quali aree del volume sono occupate.

NTFS mantiene, ad esempio, il file speciale $Bitmap, utilizzato per rappresentare i cluster liberi e utilizzati del volume.

Se il file è grande, il file system può gestire porzioni considerevoli di spazio e quindi procedere con trasferimenti relativamente lunghi e continui.

Con moltissimi file piccoli la situazione cambia: creazione, allocazione e aggiornamento dei metadati si alternano continuamente alla scrittura di quantità relativamente modeste di dati. Il risultato è un carico di lavoro profondamente diverso da quello normalmente rappresentato dal valore “scrittura sequenziale: 7.000 MB/s” stampato sulla confezione di un SSD NVMe.

Perché i 7 GB/s dell’SSD sembrano improvvisamente sparire

Le velocità più spettacolari dichiarate per gli SSD più evoluti riguardano generalmente trasferimenti sequenziali effettuati con blocchi sufficientemente grandi e con un certo livello di parallelismo. Sono condizioni ideali per saturare il collegamento PCIe e sfruttare il controller NAND.

La copia di migliaia o milioni di piccoli file è invece molto più vicina a un carico di lavoro composto da numerose operazioni separate.

In questo caso contano parametri come IOPS (Input/Output Operations Per Second) e soprattutto il tempo necessario per completare ogni operazione. Un SSD NVMe resta enormemente più rapido di un hard disk meccanico, ma anch’esso non può eliminare il lavoro svolto dal file system e dal sistema operativo.

Il dispositivo di memorizzazione può essere velocissimo nel trasferimento dei dati e contemporaneamente trascorrere buona parte del tempo aspettando che il software gli presenti il prossimo blocco da scrivere.

L’apporto della cache e il ruolo dell’antivirus

Windows utilizza una cache per ridurre il numero di accessi diretti all’unità. Per impostazione predefinita, una lettura può essere soddisfatta dalla RAM e una scrittura può inizialmente finire nella cache, per essere trasferita successivamente sul dispositivo attraverso il meccanismo di write-back. Microsoft specifica inoltre che la cache è gestita per ciascun oggetto file.

È anche uno dei motivi per cui una copia può sembrare velocissima nei primi secondi per poi rallentare. All’inizio una parte significativa dei dati può transitare attraverso RAM e cache dell’unità. Quando i buffer disponibili si riempiono, il trasferimento deve adeguarsi alla reale velocità sostenibile dello storage.

Con i file di piccole dimensioni, la cache aiuta, soprattutto per i metadati e per le letture ripetute, ma non annulla la necessità di creare oggetti distinti, aggiornare directory e mantenere coerenti le strutture del file system.

C’è poi un componente che spesso si dimentica quando si confrontano i tempi di copia: l’antivirus. Una soluzione di protezione in tempo reale può intercettare le operazioni sul file system e analizzare i file mentre vengono letti, creati o modificati. Il costo di una singola verifica può essere minimo, ma il problema è ancora una volta la moltiplicazione.

Migliaia di semplici file di testo non richiedono lo stesso lavoro di migliaia di archivi, eseguibili, script o documenti che il motore di sicurezza vuole esaminare più attentamente.

A conferma dell’importanza della problematica, Microsoft mette a disposizione per Defender un apposito Performance Analyzer capace di indicare file, estensioni, percorsi e processi che determinano il maggiore impatto durante le scansioni antivirus.

Su un NAS il problema diventa ancora più evidente

Le differenze esplodono quando origine e destinazione non si trovano sullo stesso PC ma si comunica con un NAS o un file server attraverso condivisioni SMB.

Trasferire un grande file permette di mantenere la rete occupata quasi continuamente. Dopo la fase iniziale, una lunga sequenza di dati attraversa il collegamento e può arrivare vicino al limite fisico di 1, 2,5 o 10 Gbps, naturalmente a condizione che storage e sistemi siano abbastanza veloci.

Migliaia di piccoli file introducono invece un’altra componente: la latenza del protocollo. Per creare un file remoto il client deve inviare richieste SMB, il server deve tradurle in operazioni sul proprio file system, completarle e restituire le risposte. Dopo la scrittura il file deve essere chiuso e la procedura ricomincia con quello successivo.

Microsoft descrive proprio la creazione di file come un’operazione relativamente costosa sia per SMB sia per il file system e osserva che, con centinaia di migliaia o milioni di piccoli file, si può trascorrere più tempo nella creazione degli oggetti che nel trasferimento dei loro dati.

Robocopy e il multithreading: nascondere parte della latenza

Su Windows esiste una possibilità molto efficace: usare Robocopy invece della normale copia tramite Esplora file.

Robocopy, acronimo di Robust File Copy, è uno strumento da riga di comando incluso in Windows e pensato per copie affidabili di file e directory, anche molto grandi o complesse. Rispetto a Esplora file offre controlli più avanzati su nuovi tentativi di trasferimento, esclusioni, sincronizzazione e soprattutto copia multithread, caratteristica utile quando bisogna trasferire grandi quantità di piccoli file.

La seguente semplice sintassi consente, ad esempio, di gestire fino a 16 operazioni di copia in parallelo:

robocopy sorgente destinazione /E /MT:16

Il vantaggio del multithreading non consiste nel rendere magicamente più veloce la creazione del singolo file. Permette piuttosto di sovrapporre le attese.

Mentre un thread aspetta la conclusione della creazione di un file, un altro può trasferire dati; mentre un’operazione attende una risposta dal server SMB, altre possono continuare.

Microsoft raccomanda proprio l’uso dell’opzione /MT nei trasferimenti di molti piccoli file. Il valore predefinito di Robocopy è 8 thread e il comando accetta un valore fino a 128, ma aumentare indiscriminatamente il numero non garantisce migliori prestazioni: troppa concorrenza può saturare CPU, storage o server e peggiorare il risultato.

Esiste inoltre l’opzione /J, che abilita l’I/O non bufferizzato, ma Microsoft la consiglia specificamente per i file di grandi dimensioni.

Anche visualizzare i nomi dei file ha un costo

C’è un dettaglio ancora più curioso: durante una copia tramite riga di comando, produrre continuamente output con il nome di ogni file richiede a sua volta lavoro. Con poche decine di elementi è irrilevante; con centinaia di migliaia di file da trasferire può contribuire ad allungare le attese.

Microsoft consiglia, nei trasferimenti SMB con Robocopy, di reindirizzare l’output con /LOG: è un altro esempio dello stesso principio. Un’operazione minuscola diventa importante quando è ripetuta molteplici volte.

Controllare se SSD o hard disk sono davvero saturi

Quando la copia sembra lenta, la prima tentazione è attribuire il problema allo storage. Il Task Manager di Windows (CTRL+MAIUSC+ESC) può però mostrare una situazione apparentemente strana: velocità di trasferimento modesta ma tempo di attività del disco vicino al 100%.

Si può controllare accedendo alla sezione Prestazioni, Disco del Task Manager: se un’unità SSD lavora al 100% ma trasferisce soltanto pochi MB/s durante la copia di migliaia di piccoli file, non significa necessariamente che l’unità abbia un problema. Il dispositivo può essere impegnato a soddisfare una quantità elevata di piccole richieste anziché trasferire lunghi blocchi sequenziali.

Prestazioni copia disco Task Manager

Ancora più indicativo è Monitoraggio risorse, avviabile premendo Windows+R e digitando resmon. Nel riquadro Disco si possono osservare i processi che stanno eseguendo I/O e i file interessati.

Monitoraggio risorse disco

Se durante la copia compaiono continuamente operazioni sul percorso sorgente e destinazione con valori di throughput relativamente bassi, il carico è probabilmente composto da molte richieste brevi.

La soluzione più semplice: creare un archivio prima della copia

Quando bisogna trasferire centinaia di migliaia di piccoli file, soprattutto verso un NAS o un altro computer, uno dei sistemi più efficaci consiste nel raccoglierli temporaneamente in un unico archivio. Non è neppure necessario comprimere i dati.

Con 7-Zip, ad esempio, si può creare un archivio senza compressione usando la seguente sintassi:

7z a -mx=0 dati.7z C:\Dati\*

-mx=0 disattiva la compressione: il programma deve essenzialmente impacchettare i file in un unico contenitore. A questo punto si trasferisce dati.7z
anziché centinaia di migliaia di elementi indipendenti. Una volta copiato l’archivio sul dispositivo di destinazione lo si estrae localmente.

Il vantaggio è particolarmente netto via rete: SMB deve trasferire un grande flusso sequenziale anziché gestire migliaia di operazioni remote di apertura, creazione e chiusura. È vero che poi il file system di destinazione dovrà comunque creare ogni singolo elemento durante l’estrazione, ma l’operazione avviene in locale e non deve pagare per ogni file anche la latenza della rete.

Un’alternativa consiste nell’usare il formato TAR: Windows 11 include di default l’utilità tar e si può creare un archivio con il comando tar -cf dati.tar C:\Dati.

Dopo la copia, sull’unità di destinazione, basta ricorre al comando tar -xf dati.tar per estrarre i dati. TAR non comprime per impostazione predefinita e richiede quindi poca CPU.

Come verificare se il problema sono davvero i file piccoli

Se una cartella da decine o centinaia di gigabyte contiene centinaia di migliaia di file con dimensioni medie nell’ordine di pochi KB o poche centinaia di KB, ci si trova esattamente nello scenario descritto in precedenza. Per capire meglio la distribuzione dei file si può usare la seguente cmdlet PowerShell:

Get-ChildItem "C:\Dati" -File -Recurse |
Group-Object {
    if ($_.Length -lt 4KB) {
        "<4 KB" } elseif ($_.Length -lt 64KB) { "4-64 KB" } elseif ($_.Length -lt 1MB) { "64 KB-1 MB" } else { ">=1 MB"
    }
} |
Select-Object Name, Count

Se la maggioranza degli elementi ricade nelle prime due fasce, il numero dei file merita più attenzione della dimensione complessiva. Ovviamente, prima di incollare lo script, è necessario ricordarsi di sostituire la cartella C:\Dati con il percorso da esaminare.

Ti consigliamo anche

Link copiato negli appunti