JMH tests the performance of AtomicLong and LongAdder

The difference between AtomicLong and Long Adder

AtomicLong

1. AtomicLong uses the underlying CAS operations to provide concurrency.
2. In the environment of low concurrency, the probability of thread collision is relatively small, and the number of spins is not much. However, in high concurrent environment, N threads spin simultaneously, which will result in a large number of failures and continuous spinning. At this time, AtomicLong's spin will become a bottleneck.
3. There is an internal volatile variable value in AtomicLong that holds the actual long value. All operations are directed at that variable. That is to say, in high concurrency environment, value variable is actually a hot spot, that is, N threads compete for a hot spot.

LongAdder

1. The basic idea of LongAdder is to disperse the hot spots. Divide the value into an array. Different threads will hit different slots in the array. Each thread only CAS the value in its own slot, so the hot spots will be dispersed and the probability of conflict will be much smaller. If you want to get the true long value, just add the values of variables in each slot back.
2. AtomicLong is sufficient for low concurrency and general business scenarios. LongAdder may be more appropriate if there is a lot of concurrency and a lot of writing and reading.

Testing the performance of AtomicLong and LongAdder with JMH

1. Create a Maven project with Pom.xml code as follows

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jane</groupId>
    <artifactId>jmh1</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- JMH -->
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>1.20</version>
        </dependency>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>1.20</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>run-benchmarks</id>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <configuration>
                            <classpathScope>test</classpathScope>
                            <executable>java</executable>
                            <arguments>
                                <argument>-classpath</argument>
                                <classpath />
                                <argument>org.openjdk.jmh.Main</argument>
                                <argument>.*</argument>
                            </arguments>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

2. Startup file

package com.jane;

import org.openjdk.jmh.annotations.*;
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;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;

@BenchmarkMode(Mode.AverageTime) // The average execution time of the test method is changed to Mode.Throughput if swallowed.
@OutputTimeUnit(TimeUnit.MICROSECONDS) // The time granularity of the output is microsecond
public class Main {
    private static AtomicLong count = new AtomicLong();
    private static LongAdder longAdder = new LongAdder();

    @Benchmark
    @Threads(1) //How many threads are started per second by default for testing
    public void atolong(){
        count.getAndIncrement(); //Testing AtomicLong Incremental Method
    }

    @Benchmark
    @Threads(1) //How many threads are started per second by default for testing
    public void loadder(){
        longAdder.increment();//Testing LongAdder Incremental Method
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(Main.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(options).run();
    }
}

3. Performance comparison under single thread

Throughput: AtomicLong performs better

Average time consumed: AtomicLong takes less time

Performance comparison under 4 and 20 threads

Throughput: LongAdder has obvious advantages

Average consumption time: LongAdder has obvious advantages

Keywords: Java Maven Apache xml

Added by Varma69 on Wed, 21 Aug 2019 14:14:55 +0300