So messen Sie die Ausführungsgeschwindigkeit von Java-Programmen

83

Wie planen Sie die Ausführung eines Java-Programms? Ich bin mir nicht sicher, in welcher Klasse ich das machen soll.

Ich suche etwas wie:

// Some timer starts here
for (int i = 0; i < length; i++) {
  // Do something
}
// End timer here

System.out.println("Total execution time: " + totalExecutionTime);
George Mic
quelle

Antworten:

139
final long startTime = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
  // Do something
}
final long endTime = System.currentTimeMillis();

System.out.println("Total execution time: " + (endTime - startTime));
bua
quelle
2
es sollte eigentlich nanoTime sein
Eugene
6
Sollte nicht nanoTime sein. Siehe Rhus Antwort.
Fabspro
4
Gibt es einen bestimmten Grund, warum Sie hier "final" verwendet haben? Was wäre anders, wenn Sie dieses Schlüsselwort löschen würden?
Dijxtra
9
@dijxtra using finalhat den Vorteil, dass Sie es nicht versehentlich zuweisen können (sowie andere Vorteile wie anonymen Klassenzugriff usw.). In diesem Code würde es keinen Unterschied machen. Es ist eine ziemlich übliche Praxis, alles zu machen finalund es nur zu finalentschlüsseln, wenn es nötig ist.
Wchargin
1
Der zu testende Code sollte zusammen mit den Zeitmessungen mehrmals ausgeführt und das erste Ergebnis verworfen werden. Die erste beinhaltet wahrscheinlich Ladezeiten für Klassen usw. Wenn Sie sie mehrmals ausführen, erhalten Sie natürlich auch einen Durchschnitt, der hilfreicher und zuverlässiger ist.
Arun Ramakrishnan
34

Beachten Sie, dass es einige Probleme System#nanoTime()gibt, die bei Multi-Core-CPUs nicht zuverlässig zum Aufzeichnen der verstrichenen Zeit verwendet werden können. Jeder Core verfügt über einen eigenen TSC ( Time Stamp Counter) ): Dieser Zähler wird verwendet, um die Nano-Zeit zu ermitteln (tatsächlich ist dies der Fall) die Anzahl der Ticks seit dem Start der CPU).

Wenn das Betriebssystem keine TSC-Zeitverzerrung durchführt, um die Kerne synchron zu halten, kann die relative Zeit sporadisch rückwärts springen, wenn ein Thread beim ersten Lesen auf einem Kern geplant und dann auf einen anderen Kern umgeschaltet wird und vorwärts.

Ich habe dies vor einiger Zeit bei AMD / Solaris beobachtet, wo verstrichene Zeiten zwischen zwei Zeitpunkten manchmal entweder als negative Werte oder als unerwartet große positive Zahlen zurückkamen. Es war ein Solaris-Kernel-Patch und eine BIOS-Einstellung erforderlich, um AMD PowerNow! aus, was es zu lösen schien.

Außerdem gibt es (AFAIK) einen bislang nicht behobenen Fehler bei der Verwendung von Java System#nanoTime()in einer VirtualBox-Umgebung. Dies verursacht für uns alle möglichen bizarren intermittierenden Threading-Probleme, da ein Großteil des java.util.concurrencyPakets auf Nano-Zeit angewiesen ist.

Siehe auch:

Ist System.nanoTime () völlig nutzlos? http://vbox.innotek.de/pipermail/vbox-trac/2010-January/135631.html

rhu
quelle
9

Sie können nutzen System#nanoTime(). Holen Sie es sich vor und nach der Ausführung und rechnen Sie einfach. Es wird oben bevorzugt, System#currentTimeMillis()weil es eine bessere Präzision hat. Abhängig von der verwendeten Hardware und Plattform kann es ansonsten zu einer falschen Lücke in der verstrichenen Zeit kommen. Hier mit Core2Duo unter Windows kann zwischen ca. 0 und ~ 15ms eigentlich nichts berechnet werden.

Ein fortgeschritteneres Tool ist ein Profiler .

BalusC
quelle
Der Timer unter Windows hat standardmäßig keine besonders gute Auflösung. Es gibt auch einen Hochleistungstimer, aber es ist viel schwieriger, ihn auch von C aus zu verwenden, und Java (AFAIK) bietet keinen Zugriff auf ein so geringes Maß an Hackery ohne JNI-Thunk.
Donal Fellows
1
nanoTime()hat ein Problem (zumindest unter Windows); Der Zeitstempel ist spezifisch für den Prozessorkern. Ich hatte ein Programm, das eine negative Ausführungszeit hatte, weil es den Zeitstempel "Start" auf einem Kern und den Zeitstempel "Stopp" auf einem anderen Kern hatte.
Gustafc
8

Sie erhalten die aktuelle Systemzeit in Millisekunden:

final long startTime = System.currentTimeMillis();

Dann tun Sie, was Sie tun werden:

for (int i = 0; i < length; i++) {
  // Do something
}

Dann sehen Sie, wie lange es gedauert hat:

final long elapsedTimeMillis = System.currentTimeMillis() - startTime;
Jonathan Feinberg
quelle
Die Antwort von BalusC ist ebenfalls richtig. Dies hängt von der erforderlichen Timer-Auflösung ab und davon, warum Sie das Timing benötigen.
Jonathan Feinberg
3

Für einfache Dinge kann System.currentTimeMillis () funktionieren.

Es ist tatsächlich so üblich, dass meine IDE so eingerichtet ist, dass bei Eingabe von "t0" die folgende Zeile generiert wird:

final long t0 = System.currentTimeMillis()

Für kompliziertere Dinge möchten Sie wahrscheinlich statistische Zeitmessungen wie hier verwenden (scrollen Sie ein wenig nach unten und sehen Sie sich die Zeitmessungen einschließlich Standardabweichungen usw. an):

http://perf4j.codehaus.org/devguide.html

SyntaxT3rr0r
quelle
+1 für den Hinweis auf den Auto-Generator-Code. Ich verwende die ganze Zeit eine ähnliche Anweisung und wusste nichts über das Einfügen von Codevorlagen. Ich habe gerade herausgefunden, wie man das mit Eclipse macht, und es wird definitiv helfen!
Jason
Alle Codehaus-Dienste wurden eingestellt. Ihr Link ist jetzt defekt.
NaXa
2

Mit AOP / AspectJ und @LoggableAnmerkungen unter jcabi-Aspekten können Sie dies einfach und kompakt tun:

@Loggable(Loggable.DEBUG)
public String getSomeResult() {
  // return some value
}

Jeder Aufruf dieser Methode wird an die SLF4J-Protokollierungsfunktion mit DEBUGProtokollierungsstufe gesendet . Und jede Protokollnachricht enthält die Ausführungszeit.

yegor256
quelle
2

Hier sind einige Möglichkeiten, um die Ausführungszeit in Java zu ermitteln:

1) System.nanoTime ()

long startTime = System.nanoTime();
.....your code....
long endTime   = System.nanoTime();
long totalTime = endTime - startTime;
System.out.println("Execution time in nanoseconds  : " + totalTime);
System.out.println("Execution time in milliseconds : " + totalTime / 1000000);

2) System.currentTimeMillis ()

long startTime = System.currentTimeMillis();
.....your code....
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("Execution time in milliseconds  : " + totalTime);

3) Instant.now ()

long startTime = Instant.now().toEpochMilli();
.....your code....
long endTime = Instant.now().toEpochMilli();
long totalTime = endTime - startTime;
System.out.println("Execution time in milliseconds: " + totalTime);

oder

Instant start = Instant.now();
.....your code....
Instant end = Instant.now();
Duration interval = Duration.between(start, end);
System.out.println("Execution time in seconds: " +interval.getSeconds());

4) Date.getTime ()

long startTime = new Date().getTime();
.....your code....
long endTime = new Date().getTime();
long totalTime = endTime - startTime;
System.out.println("Execution time in milliseconds: " + totalTime);
SumiSujith
quelle
1

lange verwenden startTime=System.currentTimeMillis() für die Startzeit am oberen Rand der Schleife

stellen long endTime= System.currentTimeMillis(); außerhalb des Endes der Schleife. Sie müssen die Werte subtrahieren, um die Laufzeit in Millisekunden zu erhalten.

Wenn Sie Zeit in Nanosekunden wünschen, schauen Sie sich das an System.nanoTime()

Jason
quelle
1

Ich habe eine Funktion höherer Ordnung erstellt, die den Code verwendet, den Sie in / als Lambda messen möchten:

class Utils {

    public static <T> T timeIt(String msg, Supplier<T> s) {
        long startTime = System.nanoTime();
        T t = s.get();
        long endTime = System.nanoTime();
        System.out.println(msg + ": " + (endTime - startTime) + " ns");
        return t;
    }

    public static void timeIt(String msg, Runnable r) {
       timeIt(msg, () -> {r.run(); return null; });
    }
}

Nennen Sie es so:

Utils.timeIt("code 0", () ->
        System.out.println("Hallo")
);

// in case you need the result of the lambda
int i = Utils.timeIt("code 1", () ->
        5 * 5
);

Ausgabe:

Code 0: 180528 ns
Code 1: 12003 ns

Besonderer Dank geht an Andy Turner , der mir geholfen hat, die Redundanz zu verringern. Siehe hier .

Willi Mentzel
quelle
0

Sie können auch Perf4J ausprobieren. Es ist eine gute Möglichkeit, das zu tun, wonach Sie suchen, und hilft bei aggregierten Leistungsstatistiken wie Mittelwert, Minimum, Maximum, Standardabweichung und Transaktionen pro Sekunde über einen festgelegten Zeitraum. Ein Auszug aus http://perf4j.codehaus.org/devguide.html :

StopWatch stopWatch = new LoggingStopWatch();

try {
    // the code block being timed - this is just a dummy example
    long sleepTime = (long)(Math.random() * 1000L);
    Thread.sleep(sleepTime);
    if (sleepTime > 500L) {
        throw new Exception("Throwing exception");
    }

    stopWatch.stop("codeBlock2.success", "Sleep time was < 500 ms");
} catch (Exception e) {
    stopWatch.stop("codeBlock2.failure", "Exception was: " + e);
}

Ausgabe:

INFO: start[1230493236109] time[447] tag[codeBlock2.success] message[Sleep time was < 500 ms]
INFO: start[1230493236719] time[567] tag[codeBlock2.failure] message[Exception was: java.lang.Exception: Throwing exception]
INFO: start[1230493237286] time[986] tag[codeBlock2.failure] message[Exception was: java.lang.Exception: Throwing exception]
INFO: start[1230493238273] time[194] tag[codeBlock2.success] message[Sleep time was < 500 ms]
INFO: start[1230493238467] time[463] tag[codeBlock2.success] message[Sleep time was < 500 ms]
INFO: start[1230493238930] time[310] tag[codeBlock2.success] message[Sleep time was < 500 ms]
INFO: start[1230493239241] time[610] tag[codeBlock2.failure] message[Exception was: java.lang.Exception: Throwing exception]
INFO: start[1230493239852] time[84] tag[codeBlock2.success] message[Sleep time was < 500 ms]
INFO: start[1230493239937] time[30] tag[codeBlock2.success] message[Sleep time was < 500 ms]
INFO: start[1230493239968] time[852] tag[codeBlock2.failure] message[Exception was: java.lang.Exception: Throwing exception]
dotrc
quelle
0
public class someClass
{
   public static void main(String[] args) // your app start point
   {
       long start = java.util.Calendar.getInstance().getTimeInMillis();

       ... your stuff ...

       long end = java.util.Calendar.getInstance().getTimeInMillis();
       System.out.println("it took this long to complete this stuff: " + (end - start) + "ms");
   }
}
luis.espinal
quelle
0

Die Verwendung von System.currentTimeMillis () ist der richtige Weg, dies zu tun. Wenn Sie jedoch die Befehlszeile verwenden und das gesamte Programm ungefähr und schnell zeitlich festlegen möchten, denken Sie an Folgendes:

time java App

Dadurch können Sie den Code und die Zeit Ihrer App nicht ändern.

MarAja
quelle
Dies hängt davon ab, wie Sie den Code ausführen. Wenn es sich um einen Code handelt, auf dem ein Server ausgeführt wird, wird die falsche Startzeit angegeben.
Arun Ramakrishnan