Benchmarking spring boot application with JMH
Asked Answered
L

2

17

I have a spring boot application which I want to benchmark using JMH. Any reference for this integration will be useful.

Lieutenant answered 3/1, 2017 at 7:33 Comment(3)
IMHO jmh isn't suitable for benchmarking applications but only for particular methods.Sculptor
I manged to do bechmarking for spring MVC application.They should be way to do it for Spring-boot appsLieutenant
spring-cloud-sleuth benchmark samplesConnotation
L
17

The solution was quite than easy than I thought. The important part is to start the spring-boot application when the benchmark is getting initialized. Define a class level variable for configuration context and give a reference to it during setup of the benchmark. Make a call to the bean method inside the benchmark.

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;

@BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MINUTES)
@State(Scope.Thread)
public class ProcessFeedBenchMark   {

    public static void main(String args[]) throws Exception {
        URLClassLoader classLoader = (URLClassLoader) ProcessFeedBenchMark.class.getClassLoader();
        StringBuilder classpath = new StringBuilder();
        for(URL url : classLoader.getURLs())
            classpath.append(url.getPath()).append(File.pathSeparator);
        classpath.append("/D:/work/zymespace/benchmark/src/main/resources/").append(File.pathSeparator);
        System.out.print(classpath.toString());
        System.setProperty("java.class.path", classpath.toString());

        Options opt = new OptionsBuilder()
        .include(ProcessFeedBenchMark.class.getName() + ".*")
        .timeUnit(TimeUnit.MILLISECONDS)
        .threads(1)

        .shouldFailOnError(true)
        .shouldDoGC(true)
        .build();

        new Runner(opt).run();
    }
    static ConfigurableApplicationContext context;

    private BenchmarkTestService service;

    @Setup (Level.Trial) 
    public synchronized void  initialize() {
        try {
            String args = "";
            if(context == null) {
                context = SpringApplication.run(BenchmarkSpringBootStater.class, args );
            }
            service = context.getBean(BenchmarkTestService.class);
            System.out.println(service);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    @Benchmark 
    public void benchmark1 (ProcessFeedBenchMark state, Blackhole bh) {
        try {
            service.li();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Lieutenant answered 6/1, 2017 at 6:11 Comment(4)
I would not rely on a number that you get out of this benchmark. You probably rely on the service configuration and any proxy mechanism. Also, most of the time spend will be for printing the result. Rather, you should return the service value from the method and let JMH handle the escape. This is not how you should use JMH.Dismast
The System.out.println statement is removed to get the right benchmark for li() method.Lieutenant
@SoumyajitSwain could you please tell me how you managed to include spring boot fat jar as dependency of JMH project?Lutestring
@VolodymyrBakhmatiuk - This line does the trick. System.setProperty("java.class.path", classpath.toString());Lieutenant
H
0

There is no need to spin up the Spring context to benchmark a method. In fact, I'm not sure what are you really trying to benchmark here as the method implementation isn't changing. If all you want is to time the method, it's a whole lot simpler and less error-prone to call the method directly. If you want to benchmark the startup time under varying circumstances, see my answer here.

Headcheese answered 5/4, 2018 at 2:10 Comment(1)
If you have references to spring beans within your method, it is not possible to directly call the method, otherwise there is no need as you mentioned.Tasset

© 2022 - 2024 — McMap. All rights reserved.