Quando un cliente dice “ho testato due versioni del programma, e quella che sembra più veloce dà risultati peggiori”, il problema spesso non è il programma, ma il benchmark.
Nello sviluppo di app Windows, ci sono troppe cose che “fanno rumore” intorno alla misurazione. Il programma A sembra più veloce, ma il benchmark dice il contrario. Al contrario, il programma B fa un bel punteggio in un loop sintetico, ma nella pratica sembra lento. In questi casi, la maggior parte delle volte non c’è contraddizione: semplicemente il benchmark sta misurando qualcosa di diverso da ciò che conta all’utente.
In questo articolo non forniamo uno strumento di benchmark, ma organizziamo le decisioni da prendere prima di far partire il cronometro.
1. La conclusione prima di tutto
- Il vero obiettivo del benchmark non è “chi fa più punti”, ma “cosa si vuole migliorare per l’utente”.
- Se quello che vuoi misurare è “il tempo dall’azione dell’utente al risultato”, non serve wall-clock rispetto a un singolo metodo; serve misurare l’intero percorso.
- Le app Windows desktop hanno molti fattori esterni: antimalware, indicizzazione, notifiche, pianificazione CPU, Wi-Fi, aggiornamenti, stato della batteria.
- Per questo, non confrontare una singola esecuzione: confrontare la distribuzione sotto condizioni registrate.
- La differenza tra CPU time e wall-clock è importante. A volte un programma sembra più veloce non perché abbia fatto meno lavoro, ma perché ha lavorato asincrono o ha nascosto l’attesa.
- In conclusione, il benchmark più pericoloso è quello che dà un singolo numero e sembra oggettivo.
2. Cosa si sta confrontando davvero?
Prima di tutto, chiariamo i due concetti base.
2.1. Confronto tra versioni di uno stesso programma
In genere si vuol sapere se un refactoring o un aggiornamento ha migliorato o peggiorato qualcosa. L’ipotesi è che il carico di lavoro sia più o meno lo stesso.
flowchart LR
V1["Versione 1.0"]
V2["Versione 1.1"]
Task["Stesso compito"]
V1 --> Task
V2 --> Task
Qui la cosa importante è fissare lo stesso input, lo stesso percorso, le stesse condizioni. Se il programma 1.1 è più lento ma fa più cose, non è un benchmark leale.
2.2. Confronto tra programmi diversi
A volte il benchmark è tra due programmi che fanno lo stesso lavoro, magari con architetture diverse. In questo caso, bisogna definire cosa significhi “stesso lavoro”. Ad esempio, se uno produce un output immediato e non mostra progresso, mentre l’altro sembra più lento ma il risultato finale arriva prima, la metrica “prima risposta” dà giudizi diversi dalla metrica “completamento”.
flowchart LR
A["Programma A"]
B["Programma B"]
T["Compito comparabile"]
A --> T
B --> T
La cosa più importante qui è non prendere per buono che “stesso input” implichi “stesso obiettivo”. Bisogna specificare l’output, il tempo misurato e l’ambiente.
3. Scegliere la metrica giusta
Molti problemi di benchmark nascono dal fatto che si misura una cosa e si discute di un’altra.
3.1. Wall-clock time
Il tempo trascorso dal punto di vista dell’utente. Include CPU, I/O, attese, context switch, tutto.
Va bene quando:
- vuoi la sensazione utente;
- vuoi confrontare scenari end-to-end.
Attenzione a:
- rumore di sistema;
- differenze di hardware;
- varianza causata da background.
3.2. CPU time
Il tempo in cui la CPU è effettivamente occupata a eseguire il processo.
Va bene quando:
- vuoi misurare se una modifica algoritmica ha ridotto il lavoro computazionale;
- vuoi isolare il costo del programma da fattori esterni.
Non va bene quando:
- vuoi la sensazione di latenza, perché esclude attese I/O o scheduling;
- il programma parallelizza o fa asincrono.
3.3. I/O time / throughput
Tempo speso su disco, rete, database.
Va bene quando:
- il collo di bottiglia è chiaramente I/O;
- vuoi misurare quanti record al secondo processi, quante richieste servisci.
3.4. Time-to-visible / time-to-interactive
Quanto passa dall’azione dell’utente al momento in cui vede qualcosa a schermo.
Va bene quando:
- vuoi misurare percezione di reattività;
- l’app ha splash screen, finestre, progress bar.
3.5. Il problema con una sola metrica
Un singolo numero non basta quasi mai. La sensazione utente è multidimensionale:
- Il programma A mostra subito una finestra, ma impiega 10 secondi a caricare i dati.
- Il programma B impiega 2 secondi a mostrare la finestra, ma poi è immediato.
- Quale è più veloce? Dipende da cosa conta per l’utente in quel task.
flowchart LR
A["Programma A"] -->|1s| V["Finestra visibile"]
V -->|10s| C["Dati completi"]
B["Programma B"] -->|2s| V2["Finestra visibile"]
V2 -->|0s| C2["Dati completi"]
Per questo, registrare più punti temporali lungo il percorso dà un quadro molto più fedele di un singolo punteggio finale.
4. Fonti di disturbo comuni su Windows
Windows non è un sistema che si ferma quando si lancia un benchmark. Ecco alcuni dei fattori più comuni che distorcono le misurazioni.
4.1. Antimalware e sicurezza
Microsoft Defender e altri software di sicurezza possono scansionare i file eseguibili, le DLL caricate o le scritture su disco. Questo è bene per la sicurezza, ma un disastro per la riproducibilità.
flowchart LR
Bench["Avvio benchmark"] --> AV["Antimalware intercetta"]
AV -->|scansione in background| Rit["Ritardo variabile"]
In un benchmark tra versioni, se una versione ha un nuovo eseguibile o DLL non visti prima, l’AV potrebbe rallentare solo quella versione.
4.2. Indicizzazione della ricerca
L’indicizzazione di Windows Search può attivarsi quando vengono creati o modificati file. Se il benchmark genera file temporanei, l’indicizzatore può attaccarsi e falsare i tempi di I/O.
4.3. Notifiche e app in background
Microsoft Teams, Outlook, OneDrive, aggiornamenti, notifiche. Questi processi competono per CPU e I/O. Anche una singola notifica può causare un picco di attività nel momento sbagliato.
4.4. Pianificazione CPU e power management
Su CPU moderne con P-core e E-core, o su laptop con risparmio energetico, lo scheduling e la frequenza della CPU cambiano continuamente. Un programma può finire su un core efficiente in un run e su un core performante in un altro.
4.5. Cache e stato precedente
La seconda esecuzione di un programma è spesso più veloce della prima perché i file sono in cache, le DLL sono già caricate, il JIT ha già compilato, il database ha le pagine in buffer. Se non si controlla, si confrontano run con e senza cache.
5. Cosa controllare prima di misurare
Ecco una checklist pratica per ridurre il rumore.
| # | Elemento | Azione |
|---|---|---|
| 1 | Build | Usa sempre build Release, non Debug |
| 2 | Avvio pulito | Riavvia il sistema o almeno chiudi le app non necessarie |
| 3 | Do Not Disturb | Disabilita notifiche durante il benchmark |
| 4 | Ricerca | Disabilita temporaneamente l’indicizzazione se il test genera molti file |
| 5 | Rete | Scollega Wi-Fi / Ethernet se non serve, per evitare sincronizzazioni |
| 6 | Alimentazione | Imposta piano di alimentazione ad alte prestazioni, collega il caricatore sui laptop |
| 7 | Processi | Registra l’elenco dei processi attivi per ogni run |
| 8 | Temperatura | Assicurati che la CPU non stia thermal-throttling |
| 9 | Ripetizioni | Esegui multipli run e guarda la distribuzione |
| 10 | Documentazione | Annota build, OS, CPU, RAM, piano energetica, processi attivi |
6. Come raccogliere i dati
6.1. Non fidarti di una singola esecuzione
Una singola misura può essere influenzata da tutto il rumore sopra. Raccogli molte osservazioni e guarda:
- mediana
- media
- deviazione standard
- percentili (p50, p90, p95, p99)
- min / max
6.2. Rappresentare la varianza
Un grafico con barre d’errore o un box plot è molto più utile di una tabella con solo medie. Se due versioni hanno media simile ma varianza diversa, l’utente percepisce molto bene quella più stabile.
flowchart LR
subgraph "Confronto tra versioni"
V1["Versione 1"]
V2["Versione 2"]
end
V1 --> D1["Distribuzione ampia\nmediocre"]
V2 --> D2["Distribuzione stretta\nveloce"]
6.3. Misurare più fasi
Dividi il percorso utente in fasi e cronometra ciascuna. Ad esempio, per l’apertura di un documento:
- Clic su file
- Processo avviato
- Finestra visibile
- UI interattiva
- Dati caricati
- Rendering completato
Se confronti due versioni solo sul totale, perdi informazioni su dove si è guadagnato o perso.
7. Casi in cui i benchmark mentono
7.1. Il benchmark sintetico non assomiglia al carico reale
Un loop che esegue un milione di volte la stessa funzione può dare numeri molto diversi da un uso reale con dati variabili, cache calde e fredde, e branching non predicibile.
7.2. Il compilatore ha ottimizzato via il codice
In benchmark micro, il compilatore JIT o AOT può eliminare il codice che pensa inutile. Assicurati di consumare il risultato.
// Esempio: il compilatore potrebbe eliminare il calcolo se non usi result
for (int i = 0; i < N; i++)
{
var result = Compute(i);
// senza usare result, l'intero loop potrebbe essere rimosso
}
7.3. La misura include costi fissi
Se misuri “tempo totale” per un’operazione che dura pochi millisecondi, il tempo di avvio del processo o il cold start della JIT domina il risultato.
7.4. Confrontare run con stati termici diversi
Su laptop, il primo run può essere veloce perché la CPU è fredda, il decimo run può rallentare per thermal throttling. Documenta la temperatura o attendi lo stabilizzarsi.
8. Strumenti utili su Windows
| Strumento | Uso |
|---|---|
System.Diagnostics.Stopwatch |
Cronometraggio ad alta precisione nel codice |
QueryPerformanceCounter / GetProcessTimes |
Misure native di tempo e CPU |
| Windows Performance Recorder (WPR) | Traccia CPU, I/O, rete, con elevata granularità |
| Windows Performance Analyzer (WPA) | Analisi delle tracce WPR |
dotnet trace / PerfView |
Profilazione .NET |
| Process Explorer | Controlla CPU, memoria, handle, DLL caricate |
| ETW / Event Tracing for Windows | Tracciamento a basso overhead |
9. Riassunto
Per fare un benchmark tra versioni di un programma Windows che significhi qualcosa:
- Definisci cosa vuoi misurare (latenza, throughput, CPU, tempo a schermo).
- Fissa le condizioni (build Release, ambiente pulito, piano energetico, multipli run).
- Scegli la metrica adatta al tuo obiettivo, non quella più facile da ottenere.
- Misura l’intero percorso utente, non solo un metodo interno.
- Raccogli la distribuzione, non una singola osservazione.
- Guarda percentili e varianza, non solo la media.
- Documenta ambiente e fattori esterni.
- Sii sospettoso quando un benchmark sembra troppo bello o troppo strano.
Il benchmark più pericoloso è quello che dà un numero e sembra oggettivo. Un buon benchmark racconta una storia che si allinea con la sensazione dell’utente.
10. Riferimenti
- Microsoft Support: Notifications and Do Not Disturb in Windows
- Microsoft Support: Search indexing in Windows
- Microsoft Learn: Configure custom exclusions for Microsoft Defender Antivirus
- Microsoft Support: Device Security in the Windows Security App
- Microsoft Learn: QueryPerformanceCounter function
- Microsoft Learn: Acquiring high-resolution time stamps
- Microsoft Learn: GetProcessTimes function
- Microsoft Learn: QueryProcessCycleTime function
- Microsoft Learn: start command
- Microsoft Learn: SetPriorityClass function
- Microsoft Learn: SetProcessAffinityMask function
- Microsoft Learn: Processor Groups
- Microsoft Learn: Windows Performance Recorder
- Microsoft Learn: WPR Command-Line Options
Articoli correlati
Articoli recenti con gli stessi tag per approfondire argomenti vicini.
Come comprendere l'isolamento delle sessioni di Windows — Session 0, RDP ed esecuzione simultanea di più utenti
Questo articolo chiarisce il concetto di «sessione» di Windows, un argomento che genera costante confusione tra gli sviluppatori di appli...
Outsourcing e sviluppo su commissione di app Windows: cosa chiarire prima di affidare l'incarico
Prima di affidare in outsourcing o su commissione lo sviluppo di un'app Windows, ecco i punti da chiarire: revisione del software esisten...
Il curioso amore di uno sviluppatore, ovvero: come ho imparato a non preoccuparmi e ad amare Windows
Windows è complicato. Ma questa complicazione è anche quella di un sistema operativo che da decenni porta sulle spalle il lavoro reale.
PowerShell avanzato — Automatizzare in sicurezza l'analisi dei log, l'archiviazione e la reportistica
Una panoramica pratica su come usare PowerShell per l'analisi dei log, la reportistica in CSV, l'archiviazione dei log obsoleti, la conse...
Guida per liberarsi dalla dipendenza dalla modalità IE
Passi pratici per estendere in sicurezza la vita dei sistemi web interni che dipendono dalla modalità IE di Microsoft Edge riducendo cont...
Argomenti correlati
Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.
Argomenti tecnici Windows
Portale su sviluppo Windows, analisi dei problemi e valorizzazione delle risorse esistenti.
Servizi collegati all’argomento
L’articolo è direttamente collegato ai servizi seguenti.
Consulenza tecnica e revisione del progetto
La progettazione di un benchmark che rappresenti davvero la sensazione dell'utente richiede un design cosciente e una revisione del piano di misurazione; si adatta bene a una consulenza tecnica.
Analisi dei bug e delle cause
Se un benchmark dà numeri instabili o contrastanti, per isolarne la causa è naturale procedere con indagine e root cause analysis.
Sviluppo di applicazioni Windows
Le app Windows che si confrontano tra versioni hanno molti punti di interferenza (AV, indicizzazione, notifiche, pianificazione CPU, WPR), quindi il supporto implementativo su misurazione e automatizzazione rientra nello sviluppo app Windows.
Domande frequenti
Domande che ricorrono nelle consulenze sull’argomento dell’articolo.
- Perché un programma più veloce dà risultati peggiori in un benchmark?
- Perché un benchmark misura un'istantanea del sistema, non necessariamente il vero costo del programma. Se un programma sembra più veloce in uso reale ma benchmarka peggio, spesso il benchmark non sta misurando ciò che conta all'utente. Possibili cause sono: misura solo wall-clock mentre lavora asincrono; include startup influenzato da cache, AV o caricamenti differiti; esegue poche iterazioni e cattura un outlier; non tiene conto della varianza di background (notifiche, update, indexing); oppure è strettamente CPU-bound mentre l'utente percepisce latenza I/O. Iniziare a documentare cosa si vuole misurare, poi scegliere l'unità (wall clock, CPU time, I/O time, tempo a schermo, throughput) e infine ripetere e registrare la distribuzione.
- Quali fattori di ambiente Windows distorcono i benchmark?
- Molti. Le notifiche e il Do Not Disturb possono interrompere il foreground; l'indicizzazione della ricerca di Windows attiva I/O e CPU in background; Microsoft Defender e altri AV scansionano file nuovi; le app in background (OneDrive, Teams, aggiornamenti) competono per risorse; lo scaling della CPU, il turbo boost e i P/E core moderni introducono varianza termica e di scheduling. Inoltre, la priorità del processo, l'affinità CPU e il tempo di startup influenzano molto le misure. Per benchmark ripetibili: usa build Release, riavvia il sistema, chiudi app non necessarie, disabilita Do Not Disturb, scollega reti non necessarie, imposta piano di alimentazione ad alte prestazioni, lancia con priorità normale e registra temperatura / utilizzo CPU insieme ai tempi.
- Migliore wall-clock o migliore CPU time?
- Dipende da cosa si vuole misurare. Se vuoi la sensazione dell'utente (quanto ci ha messo da quando ha fatto l'azione a quando ha visto il risultato), wall-clock è corretto, a patto di misurare l'intero flusso utente. Se vuoi capire se una modifica algoritmica ha ridotto il lavoro della CPU, CPU time è più stabile perché non include attese I/O o scheduling. Nella maggior parte dei benchmark tra versioni di programmi Windows, conviene misurare entrambi e guardare la distribuzione, non la singola osservazione. Il problema spesso non è quale scegliere, ma prendere un'unica metrica e presentarla come verità.
- Quante iterazioni servono per un benchmark significativo?
- Non esiste un numero magico. La regola pratica è: ripeti finché la varianza si stabilizza e puoi stimare un intervallo di confidenza. Per programmi Windows desktop, almeno 10-30 iterazioni sono spesso necessarie per catturare la varianza di background. Scartare outlier a mano è pericoloso: meglio raccoglierne molti e guardare mediane, percentili (p50, p95, p99) e varianza. Se un test dura a lungo, usa meno iterazioni ma annota la deviazione standard. Registra anche le condizioni ambiente (build, OS, CPU, memoria, piano energia, processi attivi) così da poter riprodurre.
Profilo dell’autore
Pagina di presentazione dell’autore dell’articolo.
Go Komura
Rappresentante di KomuraSoft LLC
Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.