Measuring Execution Time of Java Methods

There are 2 ways to measure execution time of Java methods.

Wall Clock Time Vs. CPU Time

  1. Wall Clock Time: Time that you actually wait for the method to complete. This metric is useful in measuring how fast a user perceives your method to be. For desktop apps and mobile apps, this measure is rarely accurate because we don’t know what other programs the user might be running in parallel when they are using our app. But this is very useful if you are measuring performance on server applications. Because they run on our server where we can control the capabilities and load from other programs. Wall clock time is what we measure in typical performance tests of web applications.
  2. CPU Time: Time spent by the CPU executing the method. Since the CPU is shared by all processes running on a computer, this metric shows you how much of the resource your program is using – or in other words, for how much time, the CPU core was kept active by your program. In an ideal world, this would be exactly the same if you run the same code again and again, but our world is more ideal than ideal – both the JVM and the OS, execute code in quite complicated ways to optimise system usage. So there still might be differences between each execution but much less than wall clock time.
Photo by Andrew Seaman on Unsplash

Source Code

You can get the source code for this article on GitHub – https://github.com/heppydepe/measuring-execution-time-of-java-methods/tree/v1.0

The Sample Class

We will use the following RandomNumbers class and measure it’s execution time. This is a neat little class for demonstrating time measurement – we can test the object creation because it creates 100 random numbers, we can test IO operation that happens when you execute writeToFile(), and you can test string conversion through the toString() method.

package com.codecreek.methodbenchmark;

import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class RandomNumbers {
    private List<Integer> list;

    public RandomNumbers() {
        Random rand = new Random();
        list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(rand.nextInt());
        }
    }

    public void writeToFile() {
        FileWriter fileWriter = new FileWriter("output.txt");
        fileWriter.write(this.toString());
        fileWriter.close();
    }

    public String toString() {
        return list.toString();
    }
}

Measuring Wall Clock Time

Wall Clock Time can be measured using the System.nanoTime() method. Capture the elapsed time when the execution starts, then capture it again when the execution ends. There are a dozen ways of capturing system time, but I like the simple nanoTime(). For some other ways you can refer this StackOverflow answer.

try {
    for (int i = 0; i < iterations; i++) {
        startTime = System.nanoTime();
        randomNumbers.writeToFile();
        endTime = System.nanoTime();
        if (i >= skipFirst) {
            measureList.add(endTime - startTime);
        }
    }
} catch (IOException ex) {
    ex.printStackTrace();
}

System.out.println("Wall Clock Time:\t" + measureList);

Measuring CPU Time

CPU Time can be measured using the ThreadMXBean. To get an instance, we use ManagementFactory.getThreadMXBean() and then it has a method getCurrentThreadCpuTime() to get elapsed time of the current thread. Again, there are other ways of doing this, but for this article I’m just keeping to the simplest ways. You don’t need external dependencies for ThreadMXBean.

ThreadMXBean tmb = ManagementFactory.getThreadMXBean();
try {
    for (int i = 0; i < iterations; i++) {
        startTime = tmb.getCurrentThreadCpuTime();
        randomNumbers.writeToFile();
        endTime = tmb.getCurrentThreadCpuTime();
        if (i >= skipFirst) {
            measureList.add(endTime - startTime);
        }
    }
} catch (IOException ex) {
    ex.printStackTrace();
}

System.out.println("CPU Time:\t" + measureList);

Output

I ran the code on my computer and this is what I got –

Wall Clock Time:
        Stats{count=10000, mean=278124.58609999955, populationStandardDeviation=368752.52698052896, min=111579.0, max=1.258139E7}

CPU Time:
        Stats{count=10000, mean=210671.20000000033, populationStandardDeviation=86977.8137835161, min=110000.0, max=1794000.0}

I used the Stats functions from Google Guava library. Even in this relatively naive test, I could notice that the wall clock time has much greater variance than CPU time.

But I do have to reiterate. This test is practically good for my use case. But might not be for others. Because I haven’t considered what optimisations my JVM and my OS may have done while executing the code. This is more of a ‘getting-started’ kind of article. For more serious benchmarking it’s better to use tools like JMH or Google Caliper .

Simple Command Line Java Project (using Gradle)

I like to create a simple command line application whenever I want to learn a new concept/product or when I’m trying to implement a complicated logic. I never actually work on command line projects – for several years now I’ve been exclusively working on web applications. But still my go to approach for implementing something new is to first try it out in a command line program and then when I am comfortable, write it into my web application. Isolating new attempts like this helps me keep a clear mind and learn the new concept in more depth. Also it’s much more efficient – otherwise, I have to load up the entire web application just to see whether my new approach worked.

Why Not Just javac and java

Yes, it’s quite simple to just write a *.java file and compile it with javac and run it with the java command. But specifying and managing dependencies is a headache. Using a tool like Gradle or Maven would enable us to avoid that whole activity and focus on our project. So even for a one-file command line application, I will use something like Gradle.

Setup Gradle

If you have never initialised a Gradle project, chances are the you never had to install Gradle on your computer. The gradlew command that is available in Gradle projects take care of downloading and installing the required version of Gradle for that project. Well now you are going to initialise a new project. You need a standard installation of Gradle to do this.

My preferred way to install Gradle is to use SDKMAN! Just sdk install gradle and I have a clean installation of Gradle. After that do gradle -v to check the version of Gradle installed. For this tutorial, it doesn’t matter much. If you’ve installed a relatively new version it’s fine.

Create A New Project

For this example, let’s create a project called test-project. Steps to create a new project are –

  1. Create a directory – Open a terminal and type mkdir test-project and then switch into the new directory cd test-project
  2. Initialize a Gradle project – execute gradle init and then answer the questions that the script asks. For this project the answers would be –
    1. Type of project is ‘application’. ‘basic’ is basically an empty project – we don’t want that. Choose ‘application’
    2. Implementation language is ‘Java’
    3. Split into sub projects – ‘no’. We just need a simple project.
    4. Build script DSL – ‘Groovy’. Selecting Kotlin doesn’t make much of a difference anyway. Being statically typed, Kotlin makes it easier for IDEs to do fancy things like auto-complete. But Groovy is quite fine for me for this project.
    5. Test framework – ‘JUnit Jupiter’. I like JUnit 5 (also called JUnit Jupiter)
    6. Project name – Accept the default – which is the directory name test-project. You can give a different name if you like.
    7. Source package – If the project is never going to leave your computer, simply accept the default or type whatever you like. If you’re going to share it, it’s a good idea to put some unique string here. Real projects use their domain names to make package names unique – org.apache.commons etc.

Keep in mind about reserved keywords when you name things. Source package names like java.test or try.newtech etc. will fail. Because try is a reserved keyword in java and java itself is forbidden to be used in package names.

That’s it. You’ve made a neat little application for yourself to play with.

Run the Project

Simply execute ./gradlew run to run your new little application. As of today, the default project prints out the string Hello World!

You can open this folder in your favourite text editor or IDE and go about making changes. The source code would be inside the app/src/main directory.

Execute ./gradlew tasks to see what tasks are available for you to run. If like me, you are doing this to do a trial implementation of some new algorithm or library, ./gradlew run is all you need.

Get Cracking

Open the build.gradle file in your project folder to add dependencies. Use the app/src/main/java/{{source-package}}/App.java file as a starting point for your code.

Of course this is only one of the countless ways in which you could put together a stub project for trying out new things, but it’s a neat and simple way. And since it’s Gradle, pretty much all of your dependencies are easy to add into your project. And you don’t have to suffer with a bulky big project while you are trying to learn your new technology or implement your new idea.