Come faccio a scrivere un micro-benchmark corretto in Java?


Come si scrive (ed esegue) un micro-benchmark corretto in Java?

Sto cercando qui esempi di codice e commenti che illustrano varie cose a cui pensare.

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

Correlati: Il benchmarking del cronometro è accettabile?

Author: Community, 2009-02-02

11 answers

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

Regola 0: Leggi un documento affidabile sulle JVM e il micro-benchmarking. Una buona è Brian Goetz, 2005. Non aspettarti troppo dai micro-benchmark; misurano solo una gamma limitata di caratteristiche prestazionali della JVM.

Regola 1: Include sempre una fase di riscaldamento che esegue il kernel di test fino in fondo, sufficiente per attivare tutte le inizializzazioni e compilazioni prima delle fasi di temporizzazione. (Meno iterazioni sono OK nella fase di riscaldamento. La regola empirica è diverse decine di migliaia di iterazioni del ciclo interno.)

Regola 2: Funziona sempre con -XX:+PrintCompilation, -verbose:gc, ecc., quindi puoi verificare che il compilatore e altre parti della JVM non stiano facendo un lavoro inaspettato durante la tua fase di temporizzazione.

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

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

Regola 4: Essere consapevoli degli effetti di inizializzazione. Non stampare per la prima volta durante la fase di temporizzazione, poiché 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 il caricamento della classe in modo specifico (e in tal caso caricare 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 junk e ricompilare il codice, sulla base di un precedente ottimistico ipotesi 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. Ispezionare il codice da soli prima di formare teorie su ciò che rende qualcosa più veloce o più lento.

Regola 7: Ridurre il rumore nelle misurazioni. Esegui il tuo benchmark su una macchina silenziosa ed eseguilo più volte, scartando valori anomali. Utilizzare -Xbatch per serializzare il compilatore con l'applicazione e considerare l'impostazione -XX:CICompilerCount=1 per impedire che il compilatore venga eseguito in parallelo con se stesso. Fai del tuo meglio per ridurre il sovraccarico di GC, imposta Xmx (abbastanza grande) uguale a Xms e usa UseEpsilonGC se è disponibile.

Regola 8: Usa una libreria per il tuo benchmark in quanto probabilmente è più efficiente ed è già stata debuggata per questo unico scopo. Ad esempio JMH, Pinza o di Bill e Paul Eccellente Benchmark UCSD per Java.

 670
Author: 11 revs, 11 users 62%Eugene Kuleshov, 2018-08-09 09:47:17

So che questa domanda è stata contrassegnata come risposta ma volevo menzionare due librerie che ci permettono di scrivere micro benchmark

Pinza da Google

Guida introduttiva tutorial

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

JMH di OpenJDK

Guida introduttiva tutorial

  1. Evitare le insidie di benchmarking sulla JVM
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://java-performance.info/jmh /
 215
Author: Aravind R. Yarram, 2018-03-28 15:10:03

Le cose importanti per i benchmark Java sono:

  • Riscalda prima il JIT eseguendo il codice più volte prima di cronometrarlo
  • Assicurati di eseguirlo abbastanza a lungo da poter misurare i risultati in secondi o (meglio) decine di secondi
  • Anche se non è possibile chiamare System.gc() tra 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 probabilmente che sarà davvero spazzatura raccogliere nella mia esperienza.)
  • Mi piace visualizzare iterazioni e 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 altri siano segnati in modo relativo. Ciò significa che è possibile eseguire tutti gli algoritmi per un tempo prolungato, variando sia il numero di iterazioni che il tempo, ma ottenendo comunque risultati comparabili.

Sono solo nel processo di blogging sulla progettazione di un framework di benchmarking in. NET.Ho una coppia di post precedenti che potrebbe essere in grado di darti alcune idee - non tutto sarà appropriato, ovviamente, ma alcuni potrebbero esserlo.

 73
Author: Jon Skeet, 2013-04-20 08:02:53

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 in i commenti dei test di esempio .

Vedere anche:

 40
Author: assylias, 2015-12-21 07:47:26

Il benchmark dovrebbe misurare il 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 iterazioni/tempo.

 17
Author: Peter Lawrey, 2009-02-02 19:54:35

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

 14
Author: Peter Štibraný, 2009-02-02 18:00:10

Se si sta tentando di confrontare due algoritmi, eseguire almeno due benchmark su 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 trovato alcune differenze evidenti (5-10% a volte) nel runtime dello stesso algoritmo in diversi passaggi..

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

 12
Author: Kip, 2009-02-02 17:57:01

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 (di OS per i file e di 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 diverse esecuzioni nella stessa istanza JVM diventeranno più veloci e più veloce.

I miei consigli: fai in modo che il tuo benchmark esegua 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 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 nuovo Java) altrimenti gli effetti di ottimizzazione della JVM possono influenzare i test in esecuzione successivi. Non farlo esegui cose, che non vengono eseguite nella fase di riscaldamento (in quanto ciò potrebbe innescare il carico di classe e la ricompilazione).

 12
Author: Mnementh, 2009-06-23 11:32:55

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

Questo perché l'implementazione A potrebbe essere più veloce durante la maggior parte delle esecuzioni del benchmark rispetto all'implementazione B. Ma A potrebbe anche avere uno spread più alto, quindi il beneficio delle prestazioni misurato di A non avrà alcun significato se confrontato con B.

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

 7
Author: SpaceTrucker, 2013-01-21 14:04:05

Http://opt.sourceforge.net / Java Micro Benchmark-compiti di controllo necessari per determinare le caratteristiche comparative delle prestazioni del sistema informatico su piattaforme diverse. Può essere utilizzato per guidare le decisioni di ottimizzazione e per confrontare diverse implementazioni Java.

 6
Author: Yuriy, 2010-12-18 23:22:11

Per aggiungere agli altri consigli eccellenti, sarei anche consapevole di quanto segue:

Per alcune CPU (ad esempio Intel Core i5 range con TurboBoost), la temperatura (e il numero di core attualmente in uso, così come la loro percentuale di utilizzo) influisce sulla velocità di clock. Poiché le CPU sono dinamicamente clock, questo può influenzare i risultati. Ad esempio, se si dispone di un'applicazione a thread singolo, la velocità massima di clock (con TurboBoost) è superiore a quella di un'applicazione che utilizza tutti i core. Questo può pertanto interferire con i confronti delle prestazioni single e multi-threaded su alcuni sistemi. Tenete a mente che la temperatura e volatages influenzano anche quanto tempo Turbo frequenza viene mantenuta.

Forse un aspetto più fondamentalmente importante su cui hai il controllo diretto: assicurati di misurare la cosa giusta! Ad esempio, se si utilizza System.nanoTime() per eseguire il benchmark di un particolare bit di codice, inserire le chiamate all'assegnazione in luoghi che hanno senso per evitare di misurare le cose che si non sono interessato a. 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. Invece, prova quanto segue:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");
 5
Author: Sina Madani, 2017-03-19 19:21:23