Correct Way of Calculating Elapsed Time in Java
Calculating Elapsed Time
The main reason why you want to know the elapsed time of the method executions is to identify bottlenecks and long running methods, or simply if you want to let your management see how awesomely fast your methods are.
System.nanoTime()
. You cannot use System.currentTimeMillis()
, unless you don’t mind your result being wrong.Difference between nanoTime and CurrentTimeMillis
The purpose of nanoTime is to measure elapsed time, and the purpose of currentTimeMillis is to measure wall-clock time. As these are designed for specific reasons, it is important to choose the right one for your needs. The reason is that no computer’s clock is perfect. It is sometimes off and periodically needs to be corrected. This correction might either happen manually, or in the case of most machines, there’s a process that runs and continually issues small corrections to the system clock or wall-clock. These shifts tend to happen often. Another such correction happens whenever there is a leap second.
Since nanoTime’s purpose is to measure elapsed time, it is unaffected by any of these small corrections. It is what you want to use for calculating elapsed time.
You may think, “So what? This will not matter that much”, to which I say, maybe not, but overall, isn’t correct code just better than incorrect code? Besides, nanoTime is shorter to type anyway.
Basic Elapsed Time Calculation
Calculating elapsed time basically comes down to start a timer just before the method execution and at the end of the method execution. You stop the timer and calculate the difference. Here is a basic example to calculate the elapsed time.
package com.memorynotfound.time;
import java.util.concurrent.TimeUnit;
public class ElapsedTime {
public static void main(String... args) throws InterruptedException {
long startTime = System.nanoTime();
Thread.sleep(1000 * 10);
long difference = System.nanoTime() - startTime;
System.out.println("Total execution time: " +
String.format("%d min, %d sec",
TimeUnit.NANOSECONDS.toHours(difference),
TimeUnit.NANOSECONDS.toSeconds(difference) -
TimeUnit.MINUTES.toSeconds(TimeUnit.NANOSECONDS.toMinutes(difference))));
}
}
Calculating Elapsed time Between two Dates
Here is a method to calculate the elapsed time between two dates. It calculates the difference between two dates and puts the result in a HashMap containing all the TimeUnits.
package com.memorynotfound;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class ElapsedTimeBetweenDates {
public static void main(String []args) {
Date startDateTime = new Date(System.currentTimeMillis() - 123456789);
Date endDateTime = new Date();
Map<TimeUnit,Long> result = computeDiff(startDateTime, endDateTime);
System.out.println(result);
System.out.println("Days: " + result.get(TimeUnit.DAYS));
System.out.println("Hours: " + result.get(TimeUnit.HOURS));
System.out.println("Minutes: " + result.get(TimeUnit.MINUTES));
System.out.println("Seconds: " + result.get(TimeUnit.SECONDS));
System.out.println("MilliSeconds: " + result.get(TimeUnit.MILLISECONDS));
}
public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {
long diffInMilliSeconds = date2.getTime() - date1.getTime();
List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
Collections.reverse(units);
Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
long milliSecondsRest = diffInMilliSeconds;
for (TimeUnit unit : units) {
long diff = unit.convert(milliSecondsRest,TimeUnit.MILLISECONDS);
long diffInMilliSecondsForUnit = unit.toMillis(diff);
milliSecondsRest = milliSecondsRest - diffInMilliSecondsForUnit;
result.put(unit,diff);
}
return result;
}
}
Output
{DAYS=1, HOURS=10, MINUTES=17, SECONDS=36, MILLISECONDS=789, MICROSECONDS=0, NANOSECONDS=0}
Days: 1
Hours: 10
Minutes: 17
Seconds: 36
MilliSeconds: 789
Calculating Elapsed Time Revisited
The basic elapsed time calculation is ok. But we strive the DRY principle (Don’t Repeat Yourself). So I guess we can do better. Here is an example class that will manage calculating elapsed time.
package com.memorynotfound.time;
import java.util.concurrent.TimeUnit;
public class TimeWatch {
long starts;
private TimeWatch() {
reset();
}
public static TimeWatch start() {
return new TimeWatch();
}
public TimeWatch reset() {
starts = System.nanoTime();
return this;
}
public long time() {
long ends = System.nanoTime();
return ends - starts;
}
public long time(TimeUnit unit) {
return unit.convert(time(), TimeUnit.NANOSECONDS);
}
public String toMinuteSeconds(){
return String.format("%d min, %d sec", time(TimeUnit.MINUTES),
time(TimeUnit.SECONDS) - time(TimeUnit.MINUTES));
}
}
In the following code snippet we test our new TimeWatch class. We use a Thread.sleep() to mimic a method execution. After the method completes we print out some info of the method execution time.
package com.memorynotfound.time;
import java.util.concurrent.TimeUnit;
public class ElapsedTimeWatch {
public static void main(String... args) throws InterruptedException {
TimeWatch watch = TimeWatch.start();
Thread.sleep(1000 * 10);
System.out.println("Elapsed Time custom format: " + watch.toMinuteSeconds());
System.out.println("Elapsed Time in seconds: " + watch.time(TimeUnit.SECONDS));
System.out.println("Elapsed Time in nano seconds: " + watch.time());
}
}
As you can see we can print out a custom format of “x min, x sec”. You can also convert the elapsed time to any format you want using the TimeUtil. And because we use the nanoTime we can also print the elapsed nano seconds.
Elapsed Time custom format: 0 min, 10 sec
Elapsed Time in seconds: 10
Elapsed Time in nano seconds: 10030802887
public String toMinuteSeconds(){
return String.format(“%d min, %d sec”, time(TimeUnit.MINUTES),
time(TimeUnit.SECONDS) – (time(TimeUnit.MINUTES) * 60));
}
The original didn’t have the ” * 60″, so the seconds didn’t subtract correctly.
Also in the Basic Time Elapsed code there is TimeUnit.NANOSECONDS.toHours(difference) which should be changed to TimeUnit.NANOSECONDS.toMinutes(difference)