Domanda

    

Questa domanda ha già una risposta qui:

         

Voglio fare alcuni test di cronometraggio su un'applicazione Java. Questo è quello che sto facendo attualmente:

long startTime = System.currentTimeMillis();
doSomething();
long finishTime = System.currentTimeMillis();
System.out.println("That took: " + (finishTime - startTime) + " ms");

C'è qualcosa di "sbagliato" con test delle prestazioni come questo? Qual è un modo migliore?

Duplica : Il benchmarking del cronometro è accettabile?

È stato utile?

Soluzione

L'unico difetto di questo approccio è che il "reale" il tempo impiegato da doSomething () può variare notevolmente a seconda di quali altri programmi sono in esecuzione sul sistema e del suo carico. Ciò rende la misurazione delle prestazioni in qualche modo imprecisa.

Un modo più accurato per tenere traccia del tempo necessario per eseguire il codice, supponendo che il codice sia a thread singolo, è quello di esaminare il tempo della CPU impiegato dal thread durante la chiamata. Puoi farlo con le classi JMX; in particolare, con ThreadMXBean . Puoi recuperare un'istanza di ThreadMXBean da java.lang.management.ManagementFactory e, se la tua piattaforma lo supporta (la maggior parte lo fa), usa getCurrentThreadCpuTime in luogo di System.currentTimeMillis per eseguire un test simile. Ricorda che getCurrentThreadCpuTime riporta il tempo in nanosecondi, non in millisecondi.

Ecco un metodo (Scala) di esempio che potrebbe essere utilizzato per eseguire una misurazione:

def measureCpuTime(f: => Unit): java.time.Duration = {

  import java.lang.management.ManagementFactory.getThreadMXBean
  if (!getThreadMXBean.isThreadCpuTimeSupported)
    throw new UnsupportedOperationException(
      "JVM does not support measuring thread CPU-time")

  var finalCpuTime: Option[Long] = None
  val thread = new Thread {
    override def run(): Unit = {
      f
      finalCpuTime = Some(getThreadMXBean.getThreadCpuTime(
        Thread.currentThread.getId))
    }
  }
  thread.start()

  while (finalCpuTime.isEmpty && thread.isAlive) {
    Thread.sleep(100)
  }

  java.time.Duration.ofNanos(finalCpuTime.getOrElse {
    throw new Exception("Operation never returned, and the thread is dead " +
      "(perhaps an unhandled exception occurred)")
  })
}

(Sentiti libero di tradurre quanto sopra in Java!)

Questa strategia non è perfetta, ma è meno soggetta a variazioni nel carico del sistema.

Altri suggerimenti

Il codice mostrato nella domanda non è un buon codice di misurazione delle prestazioni:

  1. Il compilatore potrebbe scegliere di ottimizzare il codice riordinando le istruzioni. Sì, può farlo. Ciò significa che l'intero test potrebbe non riuscire. Può anche scegliere di incorporare il metodo in prova e riordinare le dichiarazioni di misurazione nel codice ora incorporato.

  2. L'hotspot potrebbe scegliere di riordinare le tue dichiarazioni, il codice incorporato, i risultati della cache, ritardare l'esecuzione ...

  3. Anche supponendo che il compilatore / hotspot non ti abbia ingannato, ciò che misuri è "tempo di parete". Quello che dovresti misurare è il tempo della CPU (a meno che tu non usi le risorse del sistema operativo e desideri includerle o misuri la contestazione dei blocchi in un ambiente multi-thread).

La soluzione? Usa un vero profiler. Ce ne sono molti in giro, sia per i profiler gratuiti che per le demo / prove a tempo di prove di forza commerciale.

L'uso di un profiler Java è l'opzione migliore e ti fornirà tutte le informazioni di cui hai bisogno nel codice. vale a dire Tempi di risposta, Tracce di chiamata di thread, Utilizzi di memoria, ecc.

Ti suggerirò JENSOR , un profiler Java open source, per i suoi facilità d'uso e nessun sovraccarico sulla CPU. Puoi scaricarlo, strumentare il codice e ottenere tutte le informazioni necessarie sul tuo codice.

Puoi scaricarlo da: http://jensor.sourceforge.net /

Tieni presente che la risoluzione di System.currentTimeMillis () varia tra i diversi sistemi operativi. Credo che Windows sia di circa 15 msec. Quindi se doSomething () funziona più velocemente della risoluzione temporale, otterrai un delta di 0. Puoi eseguire doSomething () in un ciclo più volte, ma poi la JVM potrebbe ottimizzarla.

Bene, questa è solo una parte del test delle prestazioni. A seconda della cosa che stai testando, potresti dover esaminare la dimensione dell'heap, il numero di thread, il traffico di rete o tutta una serie di altre cose. Altrimenti uso quella tecnica per cose semplici che voglio solo vedere quanto tempo impiegano a correre.

Va ??bene quando stai confrontando un'implementazione con un'altra o provando a trovare una parte lenta nel tuo codice (anche se può essere noioso). È una tecnica davvero buona da sapere e probabilmente la userai più di ogni altra, ma avrai anche familiarità con uno strumento di profilazione.

Hai esaminato gli strumenti di profilazione in netbeans e eclipse . Questi strumenti ti offrono una migliore gestione di ciò che REALMENTE sta occupando continuamente nel tuo codice. Ho riscontrato problemi che non ho realizzato utilizzando questi strumenti.

Immagino che vorresti fare qualcosa () prima di iniziare anche il cronometraggio, in modo che il codice sia JITted e "riscaldato".

Japex può esserti utile, sia come modo per creare rapidamente benchmark, sia come modo per studiare problemi di benchmarking in Java attraverso il codice sorgente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top