Search code examples
javajmh

How can I use JMH to benchmark a specific part of a method


I'm looking for a way to benchmark a specific for-loop inside a method. The for-loop cannot be extracted and have its state initialized/mocked.

  void method(ScheduledExecutorService exec) throws Exception {
    File testFile = new File("./test");
    Collection<Future> futures = new HashSet<>();
    testFile.createNewFile();
    for (int i = 0; i < 50; i++) {
      final int j = i;
      final Future<?> future = exec.submit(() -> {
        Files.write(("Test-" + j).getBytes(StandardCharsets.UTF_8), testFile);
      });
      futures.add(future);
    }
    // BENCHMARK HERE
    for (Future future : futures) {
      future.get();
    }
  }

Notice that I'm looking to benchmark only the future.get() part, using JMH. My research shows that JMH is like JUnit, i.e. it discovers @Benchmark annotated methods and runs those with a clean state, and also starts a new JVM process to run those benchmarks, which means we can't provide the Collection<Future> as state to a benchmarked method.

Yes I understand that I shouldn't benchmark File I/O but someone else is asking for JMH to be used instead of System.currentTimeMillis so I'm looking to see if this is even possible before going back to block this requirement.


Solution

  • JMH is method-based framework, i.e. it measures performance of a method annotated with @Benchmark, so in your case there's no other way but to split the logic. Everything before the loop should be moved into @Setup-annotated method and the the loop into @Benchmark-annotated one. Pay attention, that in this case JMH must be run in single-shot mode or @Setup must be set into Invocation mode to have the futures recreated for each measurement.