How to write the main method test elegantly? Can’t be too low!


Click above to follow “Terminal R&D Department

Set it as a "star" to master more database knowledge with you

Source: https://juejin.cn/post/6844903936869007368

Previous: Interviewer: What is the difference between int(1) and int(10)?

Hi everyone, I am Brother Yu from the Terminal R&D Department.

JMH – Java microbenchmarking tool

052bf0878406601d3c2bb56c851a35b6.jpeg

accurate-builder-equipment-1573821.jpg

Foreword

“If you cannot measure it, you cannot improve it”.

In daily development, we have many options for calling some code or using tools. When we are not sure about their performance, the first thing we want to do is to measure it. Most of the time, we will simply measure by counting multiple times to see the total time consumption of this method.

However, if you are familiar with the JVM class loading mechanism, you should know that the default execution mode of the JVM is a mixed execution of JIT compilation and interpretation. JVM identifies high-frequency method calls, loop bodies, public modules, etc. through statistical analysis of hot codes. Based on JIT dynamic compilation technology, it converts hot codes into machine codes and directly sends them to the CPU for execution.

e6c27894a4b02619bd535d4066d34d5b.jpeg

image.png

In other words, the JVM will continue to compile and optimize, which makes it difficult to determine how many times to repeat to get a stable test result? Therefore, many experienced students will write a warm-up logic before testing the code.

JMH, the full name of Java Microbenchmark Harness (micro-benchmark testing framework), is a set of test tool APIs dedicated to Java code micro-benchmark testing, and is an official tool released by OpenJDK/Oracle. What is Micro Benchmark? Simply put, it is a benchmark at the method level, and the accuracy can be as accurate as microseconds.

A few points to note when benchmarking Java:

  • Warming up is required before testing.

  • Prevent useless code from entering the test method.

  • Concurrency testing.

  • The test results are presented.

JMH usage scenarios:

  1. Quantitatively analyze the optimization effect of a hot function

  2. Want to know quantitatively how long a function takes to execute, and the correlation between execution time and input variables

  3. Comparing multiple implementations of a function

This article mainly introduces JMH’s DEMO demonstration and common annotation parameters. Hope to help you.

DEMO demo

Here is a demo first, so that students who don’t know JMH can quickly grasp the general usage of this tool.

1. Test project build

JMH is built-in Java9 and later versions. Here is an explanation with Java8.

For convenience, here is a direct introduction to the way to use maven to build a JMH test project.

The first is to use the command line to build, execute the following command in the specified directory:

$ mvn archetype:generate \
          -DinteractiveMode=false \
          -DarchetypeGroupId=org.openjdk.jmh \
          -DarchetypeArtifactId=jmh-java-benchmark-archetype \
          -DgroupId=org.sample\
          -DartifactId=test\
          -Dversion=1.0
Copy Code

A test project will appear in the corresponding directory. After opening the project, we will see such a project structure.

3c9062adb73d720915f9162662f53c79.jpeg

image.png

The second way is to directly add the dependencies of jmh-core and jmh-generator-annprocess to the existing maven project to integrate JMH.

<dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>${jmh.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>${jmh.version}</version>
            <scope>provided</scope>
        </dependency>
Copy Code

2. Write performance tests

Here I take the example of testing the performance gap between LinkedList iteration through index and foreach iteration, and write a test class. The annotations involved will be explained later.

/**
 * @author Richard_yyf
 * @version 1.0 2019/8/27
 */

@State(Scope. Benchmark)
@OutputTimeUnit(TimeUnit. SECONDS)
@Threads(Threads. MAX)
public class LinkedListIterationBenchMark {
 private static final int SIZE = 10000;

    private List<String> list = new LinkedList<>();
    
    @Setup
    public void setUp() {
        for (int i = 0; i < SIZE; i ++ ) {
            list.add(String.valueOf(i));
        }
    }

    @Benchmark
    @BenchmarkMode(Mode. Throughput)
    public void forIndexIterate() {
        for (int i = 0; i < list. size(); i ++ ) {
            list. get(i);
            System.out.print("");
        }
    }

    @Benchmark
    @BenchmarkMode(Mode. Throughput)
    public void forEachIterate() {
        for (String s: list) {
            System.out.print("");
        }
    }
}
Copy Code

3. Execute the test

There are two ways to run the JMH benchmark test, one is to run the production jar file, and the other is to directly write the main function or execute it in a unit test.

The form of generating jar files is mainly for some relatively large tests. There may be some requirements for machine performance or real environment simulation. It is necessary to write the test method and execute it in the linux environment. The specific commands are as follows

$ mvn clean install
$ java -jar target/benchmarks.jar
Copy Code
Awesome! N open source projects necessary for private work! Collect now

What we encounter in our daily life are generally some small tests, such as the example I wrote above, just run it directly in the IDE. The way to start it is as follows:

public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(LinkedListIterationBenchMark.class.getSimpleName())
                .forks(1)
                .warmupIterations(2)
                .measurementIterations(2)
            .output("E:/Benchmark.log")
                .build();

        new Runner(opt).run();
    }
Copy Code

4. Report results

The output is as follows,

Final result:

Benchmark Mode Cnt Score Error Units
LinkedListIterationBenchMark.forEachIterate thrpt 2 1192.380 ops/s
LinkedListIterationBenchMark.forIndexIterate thrpt 2 206.866 ops/s
Copy Code

the whole process:

# Detecting actual CPU count: 12 detected
# JMH version: 1.21
# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
# VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2 \bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, 10 seconds each
# Measurement: 2 iterations, 10 seconds each
# Timeout: 10 min per iteration
# Threads: 12 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forEachIterate

# Run progress: 0.00% complete, ETA 00:01:20
# Fork: 1 of 1
# Warmup Iteration 1: 1189.267 ops/s
# Warmup Iteration 2: 1197.321 ops/s
Iteration 1: 1193.062 ops/s
Iteration 2: 1191.698 ops/s


Result "org.sample.jmh.LinkedListIterationBenchMark.forEachIterate":
  1192.380 ops/s


# JMH version: 1.21
# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
# VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2 \bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, 10 seconds each
# Measurement: 2 iterations, 10 seconds each
# Timeout: 10 min per iteration
# Threads: 12 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate

# Run progress: 50.00% complete, ETA 00:00:40
# Fork: 1 of 1
# Warmup Iteration 1: 205.676 ops/s
# Warmup Iteration 2: 206.512 ops/s
Iteration 1: 206.542 ops/s
Iteration 2: 207.189 ops/s


Result "org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate":
  206.866 ops/s


# Run complete. Total time: 00:01:21

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
The benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark Mode Cnt Score Error Units
LinkedListIterationBenchMark.forEachIterate thrpt 2 1192.380 ops/s
LinkedListIterationBenchMark.forIndexIterate thrpt 2 206.866 ops/s

Copy Code

Introduction to annotations

Let us introduce the relevant annotations in detail below.

@BenchmarkMode

Microbenchmark type. JMH provides the following types of support:

Type Description
Throughput The number of executions per period of time, usually in seconds
AverageTime Average time, the average time spent on each operation
SampleTime In the test, the time for random sampling execution
SingleShotTime Calculation time spent in each execution
All All modes

Can be annotated at the method level or at the class level,

@BenchmarkMode(Mode. All)
public class LinkedListIterationBenchMark {
...
}
@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
public void m() {
...
}
Copy Code

@Warmup

This word means preheating, and iterations = 3 refers to the number of preheating rounds.

@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
@Warmup(iterations = 3)
public void m() {
...
}
Copy Code

@Measurement

The number of rounds for the formal metric computation.

  • iterations rounds for testing

  • time The duration of each round

  • timeUnit time unit

@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
@Measurement(iterations = 3)
public void m() {
...
}
Copy Code

@Threads

Test threads in each process.

@Threads(Threads.MAX)
public class LinkedListIterationBenchMark {
 ...
}
Copy Code

@Fork

The number of forks performed. If the number of forks is 3, JMH will fork 3 processes for testing.

@Benchmark
@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
@Fork(value = 3)
public void m() {
...
}
Copy Code

@OutputTimeUnit

Time type for benchmark results. Generally select seconds, milliseconds, microseconds.

@OutputTimeUnit(TimeUnit. SECONDS)
public class LinkedListIterationBenchMark {
 ...
}
Copy Code

@Benchmark

Method-level annotation, indicating that the method is an object that needs to be benchmarked, and its usage is similar to JUnit’s @Test.

@Param

Attribute-level annotations, @Param can be used to specify multiple situations of a certain parameter. It is especially suitable for testing the performance of a function under different input parameters.

@Setup

Method-level annotation, the function of this annotation is that we need to do some preparation before testing, such as initializing some data.

@TearDown

Method-level annotation, the function of this annotation is that we need to do some end work after the test, such as closing the thread pool, database connection, etc., mainly used for resource recovery, etc.

@State

When using the @Setup parameter, you must add this parameter to the class, otherwise it will prompt that it cannot run.

For example, in my example above, state must be set.

State is used to declare that a class is a “state”, and then accepts a Scope parameter to indicate the shared scope of the state. Because many benchmarks will need some classes to represent the state, JMH allows you to inject these classes into the benchmark function in the way of dependency injection. Scope is mainly divided into three types.

  1. Thread: This state is exclusive to each thread.

  2. Group: This state is shared by all threads in the same group.

  3. Benchmark: This state is shared among all threads.

Activation method

In the startup method, some parameters mentioned above can be specified directly, and the test results can be output to the specified file.

/**
     * Only for running in IDE
     * The command line mode is build and then java -jar start
     *
     * 1. This is the entry point for benchmark startup
     * 2. Some configuration work of the JMH test is also completed here
     * 3. In the default scenario, JMH will look for methods marked with @Benchmark, and the semantics of inclusion and exclusion can be completed through include and exclude
     */
    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                // contains semantics
                // You can use the method name or XXX.class.getSimpleName()
                .include("Helloworld")
                // exclusion semantics
                .exclude("Pref")
                // Warm up for 10 rounds
                .warmupIterations(10)
                // represent 10 rounds of formal metrology testing,
                // And each time, the warm-up is performed first and then the formal measurement is performed.
                // The content is to call the code marked with @Benchmark.
                .measurementIterations(10)
                // forks(3) refers to doing 3 rounds of testing,
                // Because a test cannot effectively represent the result,
                // So through 3 rounds of testing more comprehensive testing,
                // And each round is preheated first, and then formally measured.
                .forks(3)
            .output("E:/Benchmark.log")
                .build();

        new Runner(opt).run();
    }

Copy Code

Conclusion

Based on JMH, many tools and frameworks can be tested, such as log framework performance comparison, BeanCopy performance comparison, etc. For more examples, please refer to the official JMH samples[1]

The above is actually just an explanation of the use of JMH, it is recommended to read this article

JAVA Supplements – JMH and 8 Test Traps [2]

The author talks about some common code testing traps from the perspective of Java Developer, analyzes their relevance to the bottom layer of the operating system and the bottom layer of Java, and uses JMH to help everyone get rid of these traps.

Reference

  1. hg.openjdk.java.net/code-tools/…[3]

  2. www.hollishuang.com/archives/10…[4]

  3. yq.aliyun.com/articles/34…[5]

  4. openjdk.java.net/projects/co…[6]

References

[1]

https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/: https://link. juejin.cn?target=https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/

[2]

https://www.cnkirito.moe/java-jmh/: https://link.juejin.cn?target=https://www.cnkirito.moe/java-jmh/

[3]

http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/: https://link. juejin.cn?target=http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/

[4]

http://www.hollishuang.com/archives/1072: https://link.juejin.cn?target=http://www.hollishuang.com/archives/1072

[5]

https://yq.aliyun.com/articles/341539?utm_content=m_39911: https://link.juejin.cn?target=https://yq.aliyun.com/articles/341539?utm_content=m_39911

[6]

https://openjdk.java.net/projects/code-tools/jmh/: https://link.juejin.cn?target=https://openjdk.java.net/projects/code-tools/ jmh/

One last sentence (don’t prostitute, please pay attention)

Reply [idea activation] to get the activation method of idea
Reply [Java] Get java-related video tutorials and materials
Reply [SpringCloud] Get more learning materials related to SpringCloud
Reply [python] Get a full set of 0 basic Python knowledge manual
Reply [2020] Get 2020java-related interview questions tutorial
Reply [Join group] You can join the technical exchange group related to the terminal R&D department
read more
Before using Spring's BeanUtils, it is recommended that you first understand these pitfalls!

lazy-mock, a tool for lazy people to generate backend mock data

Early adopters on Huawei Hongmeng OS, my first "hello world", take off!

ByteDance side: Is i++ thread-safe?

An accident caused by SQL, the colleague was fired directly! !

Too heartbroken! Investigate that the CPU of Alibaba Cloud ECS reaches 100%

A powerful swagger-ui written by vue, a bit showy (with open source address)


Believe in yourself, there is nothing you can't do, only unexpected, you can get more than technology here!



If you like it, give it a "watching"