Come scrivo un micro-benchmark corretto in Java?

Feb 03 2009

Come scrivi (ed esegui) un micro-benchmark corretto in Java?

Sto cercando alcuni esempi di codice e commenti che illustrino varie cose a cui pensare.

Esempio: il benchmark deve misurare tempo / iterazione o iterazioni / tempo e perché?

Correlati: il benchmarking del cronometro è accettabile?

Risposte

802 12revs,12users61%EugeneKuleshov Feb 05 2009 at 03:49

Suggerimenti sulla scrittura di micro benchmark dai creatori di Java HotSpot :

Regola 0: leggi un documento attendibile su JVM e micro-benchmarking. Uno bravo è Brian Goetz, 2005 . Non aspettarti troppo dai micro-benchmark; misurano solo una gamma limitata di caratteristiche prestazionali JVM.

Regola 1: includi sempre una fase di riscaldamento che esegue il kernel di prova fino in fondo, sufficiente per attivare tutte le inizializzazioni e le compilazioni prima delle fasi di temporizzazione. (Un numero inferiore di iterazioni va bene nella fase di riscaldamento. La regola pratica è di diverse decine di migliaia di iterazioni del ciclo interno.)

Regola 2: Sempre correre con -XX:+PrintCompilation, -verbose:gce così via, in modo da poter verificare che il compilatore e altre parti della JVM non stanno facendo il lavoro imprevisto durante la fase di sincronizzazione.

Regola 2.1: Stampa messaggi all'inizio e alla fine delle fasi di cronometraggio e riscaldamento, in modo da poter verificare che non vi sia alcun output dalla Regola 2 durante la fase di cronometraggio.

Regola 3: essere consapevoli della differenza tra -cliente -server, e OSR e le compilazioni regolari. La -XX:+PrintCompilationbandiera riporta compilation OSR con un at-segno per indicare il punto di ingresso non iniziale, per esempio: Trouble$1::run @ 2 (41 bytes). Preferisci il server al client e regolare a OSR, se stai cercando le migliori prestazioni.

Regola 4: essere consapevoli degli effetti dell'inizializzazione. Non stampare per la prima volta durante la fase di temporizzazione, poiché la stampa carica e inizializza le classi. Non caricare nuove classi al di fuori della fase di riscaldamento (o fase di reporting finale), a meno che non si stia testando in modo specifico il caricamento delle classi (e in tal caso si caricano solo le classi di test). La regola 2 è la tua prima linea di difesa contro tali effetti.

Regola 5: essere consapevoli degli effetti di deottimizzazione e ricompilazione. Non prendere alcun percorso di codice per la prima volta nella fase di temporizzazione, perché il compilatore potrebbe spazzare via e ricompilare il codice, sulla base di un precedente presupposto ottimistico che il percorso non sarebbe stato utilizzato affatto. La regola 2 è la tua prima linea di difesa contro tali effetti.

Regola 6: usa strumenti appropriati per leggere la mente del compilatore e aspettati di essere sorpreso dal codice che produce. Ispeziona tu stesso il codice prima di formulare teorie su ciò che rende qualcosa più veloce o più lento.

Regola 7: ridurre il rumore nelle misurazioni. Esegui il benchmark su una macchina silenziosa ed eseguilo più volte, eliminando i valori anomali. Utilizzare -Xbatchper serializzare il compilatore con l'applicazione e valutare l'impostazione -XX:CICompilerCount=1per evitare che il compilatore venga eseguito in parallelo con se stesso. Fai del tuo meglio per ridurre l'overhead GC, impostare Xmx(abbastanza grande) uguale Xmse utilizzare UseEpsilonGCse disponibile.

Regola 8: utilizzare una libreria per il benchmark poiché è probabilmente più efficiente ed è già stata sottoposta a debug per questo unico scopo. Come JMH , Caliper o gli eccellenti benchmark UCSD di Bill e Paul per Java .

244 AravindYarram Dec 19 2010 at 06:35

So che questa domanda è stata contrassegnata come risposta, ma volevo menzionare due librerie che ci aiutano a scrivere micro benchmark

Caliper di Google

Tutorial per iniziare

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

JMH di OpenJDK

Tutorial per iniziare

  1. Evitare le insidie ​​del benchmarking sulla JVM
  2. Utilizzo di JMH per Java Microbenchmarking
  3. Introduzione a JMH
88 JonSkeet Feb 03 2009 at 00:46

Le cose importanti per i benchmark Java sono:

  • Scaldare il JIT prima eseguendo il codice più volte prima di cronometraggio che
  • Assicurati di eseguirlo abbastanza a lungo da poter misurare i risultati in secondi o (meglio) decine di secondi
  • Sebbene non sia possibile chiamare System.gc()tra le iterazioni, è una buona idea eseguirlo tra i test, in modo che ogni test possa avere uno spazio di memoria "pulito" con cui lavorare. (Sì, gc()è più un suggerimento che una garanzia, ma è molto probabile che in base alla mia esperienza possa davvero raccogliere i rifiuti.)
  • Mi piace visualizzare le iterazioni e il tempo e un punteggio di tempo / iterazione che può essere scalato in modo tale che l'algoritmo "migliore" ottenga un punteggio di 1.0 e gli altri siano valutati in modo relativo. Ciò significa che puoi eseguire tutti gli algoritmi per un tempo molto lungo, variando sia il numero di iterazioni che il tempo, ma ottenendo comunque risultati comparabili.

Sto solo scrivendo un blog sulla progettazione di un framework di benchmarking in .NET. Ho un paio di post precedenti che potrebbero darti alcune idee - non tutto sarà appropriato, ovviamente, ma alcuni potrebbero esserlo.

48 assylias Apr 03 2013 at 19:32

jmh è una recente aggiunta a OpenJDK ed è stato scritto da alcuni ingegneri delle prestazioni di Oracle. Sicuramente vale la pena dare un'occhiata.

Jmh è un cablaggio Java per la creazione, l'esecuzione e l'analisi di benchmark nano / micro / macro scritti in Java e in altri linguaggi mirati alla JVM.

Informazioni molto interessanti sepolte nei commenti dei test di esempio .

Guarda anche:

  • Evitare le insidie ​​del benchmarking sulla JVM
  • Discussione sui principali punti di forza di jmh .
23 PeterLawrey Feb 03 2009 at 02:54

Il benchmark dovrebbe misurare tempo / iterazione o iterazioni / tempo e perché?

Dipende da cosa stai cercando di testare.

Se sei interessato alla latenza , usa time / iteration e se sei interessato al throughput , usa iterations / time.

16 Kip Feb 03 2009 at 00:57

Se stai cercando di confrontare due algoritmi, esegui almeno due benchmark per ciascuno, alternando l'ordine. cioè:

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

Ho riscontrato alcune differenze evidenti (5-10% a volte) nel runtime dello stesso algoritmo in passaggi diversi ..

Inoltre, assicurati che n sia molto grande, in modo che il tempo di esecuzione di ogni ciclo sia di almeno 10 secondi circa. Più iterazioni, più cifre significative nel tempo di riferimento e più affidabili sono i dati.

15 PeterŠtibraný Feb 03 2009 at 01:00

Assicurati di utilizzare in qualche modo i risultati calcolati nel codice di benchmark. Altrimenti il ​​tuo codice può essere ottimizzato.

13 Mnementh Feb 03 2009 at 00:46

Ci sono molte possibili insidie ​​per la scrittura di micro-benchmark in Java.

Primo: devi calcolare con tutti i tipi di eventi che richiedono tempo più o meno casuale: Garbage collection, effetti di caching (del sistema operativo per i file e della CPU per la memoria), IO ecc.

Secondo: non puoi fidarti dell'accuratezza dei tempi misurati per intervalli molto brevi.

Terzo: la JVM ottimizza il codice durante l'esecuzione. Quindi esecuzioni diverse nella stessa istanza JVM diventeranno sempre più veloci.

I miei consigli: fai in modo che il tuo benchmark venga eseguito alcuni secondi, che è più affidabile di un runtime su millisecondi. Riscaldare la JVM (significa eseguire il benchmark almeno una volta senza misurare, che la JVM può eseguire le ottimizzazioni). Ed esegui il tuo benchmark più volte (forse 5 volte) e prendi il valore mediano. Esegui ogni micro-benchmark in una nuova istanza JVM (chiama per ogni benchmark un nuovo Java) altrimenti gli effetti di ottimizzazione della JVM possono influenzare i test successivi. Non eseguire cose che non vengono eseguite nella fase di riscaldamento (in quanto ciò potrebbe attivare il caricamento della classe e la ricompilazione).

8 SpaceTrucker Jan 21 2013 at 21:04

Va anche notato che potrebbe anche essere importante analizzare i risultati del micro benchmark quando si confrontano diverse implementazioni. Pertanto dovrebbe essere effettuato un test di significatività .

Questo perché l'implementazione Apotrebbe essere più veloce durante la maggior parte delle esecuzioni del benchmark rispetto all'implementazione B. Ma Apotrebbe anche avere uno spread più elevato, quindi il vantaggio in termini di prestazioni misurate Anon avrà alcun significato se confrontato con B.

Quindi è anche importante scrivere ed eseguire correttamente un micro benchmark, ma anche analizzarlo correttamente.

8 SinaMadani Mar 20 2017 at 02:21

Per aggiungere agli altri ottimi consigli, vorrei anche tenere presente quanto segue:

Per alcune CPU (ad esempio la gamma Intel Core i5 con TurboBoost), la temperatura (e il numero di core attualmente utilizzati, nonché la loro percentuale di utilizzo) influisce sulla velocità di clock. Poiché le CPU hanno un clock dinamico, ciò può influire sui risultati. Ad esempio, se si dispone di un'applicazione a thread singolo, la velocità di clock massima (con TurboBoost) è superiore a quella di un'applicazione che utilizza tutti i core. Ciò può quindi interferire con il confronto delle prestazioni a thread singolo e multi-thread su alcuni sistemi. Tieni presente che la temperatura e le volatilità influiscono anche sulla durata del mantenimento della frequenza turbo.

Forse un aspetto più fondamentale su cui hai il controllo diretto: assicurati di misurare la cosa giusta! Ad esempio, se stai usando System.nanoTime()per confrontare un particolare bit di codice, metti le chiamate all'assegnazione in posti che abbiano senso per evitare di misurare cose che non ti interessano. Ad esempio, non fare:

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

Il problema è che non ottieni immediatamente l'ora di fine quando il codice è terminato. Prova invece quanto segue:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");
7 Yuriy Dec 19 2010 at 06:22

http://opt.sourceforge.net/Java Micro Benchmark: attività di controllo necessarie per determinare le caratteristiche di prestazioni comparative del sistema informatico su piattaforme diverse. Può essere utilizzato per guidare le decisioni di ottimizzazione e per confrontare diverse implementazioni Java.