Backend Development 6 min read

Using JMH for Precise Java Microbenchmarking: Comparing System.currentTimeMillis() and System.nanoTime()

This article explains how to quickly benchmark Java code performance, introduces the JMH microbenchmarking tool, provides sample JMH benchmark code comparing System.currentTimeMillis() and System.nanoTime(), and presents the results showing nanoTime's superior throughput.

FunTester
FunTester
FunTester
Using JMH for Precise Java Microbenchmarking: Comparing System.currentTimeMillis() and System.nanoTime()

When you write Java code and need to know its performance, or when you have multiple implementation options and must choose the faster one, a quick performance test is essential.

Initially the author used a simple manual test that measured elapsed time with System.currentTimeMillis() around a loop calling doSomething() :

public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        doSomething();
    }
    long end = System.currentTimeMillis();
    System.out.println(end - start);
}

Later the test was wrapped in a custom concurrent utility:

public static void main(String[] args) {
    def test = {
        doSomething()
    }
    new FunQpsConcurrent(test, "Demo").start();
}

Recently the author discovered JMH (Java Microbenchmark Harness), a suite designed for method‑level benchmarking with nanosecond precision, created by the engineers who implement the JVM JIT. The tool is easy to start, and the author recommends spending about two hours learning it, while also checking the official GitHub samples for common pitfalls.

For IntelliJ users, a dedicated plugin simplifies JMH usage, and visualisation tools can generate graphical reports.

The author then built a JMH benchmark to compare System.currentTimeMillis() and System.nanoTime() :

package com.funtest.groovytest;

import com.funtester.frame.SourceCode;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS)
@Threads(2)
@Fork(1)
@State(value = Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class JmhT extends SourceCode {

    @Param(value = {"10", "20", "50"})
    private int length;

    @Benchmark
    public void mill() {
        System.currentTimeMillis();
    }

    @Benchmark
    public void nano() {
        System.nanoTime();
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(JmhT.class.getSimpleName())
                .result("result.json")
                .resultFormat(ResultFormatType.JSON)
                .forks(1)
                .threads(20)
                .warmupIterations(2)
                .warmupBatchSize(1)
                .measurementIterations(2)
                .measurementBatchSize(2)
                .build();
        new Runner(options).run();
    }
}

The benchmark runs with different length parameters (which are not actually used) to demonstrate JMH’s parameterisation feature. The generated JSON result can be visualised, but the raw numbers already reveal the performance difference.

Benchmark output (text version):

Benchmark  (length)   Mode  Cnt  Score   Error   Units
JmhT.mill        10  thrpt    2  0.015          ops/ns
JmhT.mill        20  thrpt    2  0.015          ops/ns
JmhT.mill        50  thrpt    2  0.016          ops/ns
JmhT.nano        10  thrpt    2  0.091          ops/ns
JmhT.nano        20  thrpt    2  0.088          ops/ns
JmhT.nano        50  thrpt    2  0.084          ops/ns

From these results, System.nanoTime() shows a much higher throughput than System.currentTimeMillis() , which may be surprising to some developers. The author notes that the visual report can be generated from the JSON file, and that IntelliJ’s JMH plugin can also produce the comparison without writing the full benchmark code.

JavaPerformance TestingMicrobenchmarkJMHSystem.nanoTime
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.