Java 如何测试同步数据之间的比较数据集的性能

Java 如何测试同步数据之间的比较数据集的性能,java,jmh,Java,Jmh,我想用CAS来改进我的代码,但我怀疑它能获得更好的性能,所以我做了一个测试。这是测试代码,这个jmh代码可靠吗 @OutputTimeUnit(TimeUnit.MILLISECONDS) @BenchmarkMode(Mode.SampleTime) @Warmup(iterations = 5) @Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS) @Threads(2

我想用CAS来改进我的代码,但我怀疑它能获得更好的性能,所以我做了一个测试。这是测试代码,这个jmh代码可靠吗

    @OutputTimeUnit(TimeUnit.MILLISECONDS)
    @BenchmarkMode(Mode.SampleTime)
    @Warmup(iterations = 5)
    @Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS)
    @Threads(20)
    @Fork(1)
    @State(Scope.Benchmark)
    public class CASBench {
        private int id=24;
        private static Object[] lockObj;
        private static AtomicReference<Integer>[] locks;
        static {
            lockObj = new Object[100];
            for (int i = 0; i < lockObj.length; i++) {
                lockObj[i] = new Object();
            }

            locks = new AtomicReference[100];
            for (int i = 0; i < locks.length; i++) {
                locks[i] = new AtomicReference<Integer>(null);
            }
        }
        @Benchmark
        public void sync() throws Exception {
            int index = id % 100;
            synchronized (lockObj[index]) {
                test();
            }
        }
        @Benchmark
        public void cas() throws Exception {
            AtomicReference<Integer> lock = locks[id % 100];
            while (!lock.compareAndSet(null, id)) {
            }
            test();
            lock.compareAndSet(id, null);
        }

        public void test() throws Exception {
            int sum=0;
            for(int i=0;i<100;i++){
                sum += i;
            }
        }
    }

我能得出这个结论吗,在这种情况下同步比较好

就我所知,你的测试确实不正确。首先,您的基准测试应该返回一个值,如示例中指定的,或者使用
黑洞

然后有两种方法来测试这一点,第一种是当存在争用而没有争用时

让我们看看在争用下会发生什么,更容易理解:

@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class Contention {

    public static void main(String[] args) throws RunnerException {

        Options opt = new OptionsBuilder()
                .jvmArgs("-ea")
                .shouldFailOnError(true)
                .include(Contention.class.getSimpleName()).build();
        new Runner(opt).run();
    }

    private AtomicInteger atomic;

    private Object lock = new Object();

    private int i = 0;

    @Setup
    public void setUp() {
        atomic = new AtomicInteger(0);
    }

    @Fork(1)
    @Threads(10)
    @Benchmark
    public int incrementAtomic() {
        return atomic.incrementAndGet();
    }

    @Fork(1)
    @Threads(10)
    @Benchmark
    public int incrementSync() {
        synchronized (lock) {
            ++i;
        }

        return i;
    }
}
代码应该是非常自解释的;这里稍作解释:

 State(Scope.Benchmark)
如果您将其更改为:
State(Scope.Thread)
每个线程都将获得自己的锁,因此此代码将被
偏置锁扭曲。
这意味着,如果您要运行以下代码:

 State(Scope.Thread)
您的输出将非常相同。大概是这样的:

Benchmark                                     Mode  Cnt   Score   Error  Units
casVSsynchronized.Contention.incrementAtomic  avgt    5  36.526 ± 6.548  ns/op
casVSsynchronized.Contention.incrementSync    avgt    5  23.655 ± 3.393  ns/op
使用以下工具运行它:

@State(Scope.Benchmark)
显示了完全不同的图片竞争下CAS的性能更好,从结果中可以看出:

Benchmark                                     Mode  Cnt    Score    Error  Units
casVSsynchronized.Contention.incrementAtomic  avgt    5  212.997 ± 42.902  ns/op
casVSsynchronized.Contention.incrementSync    avgt    5  457.896 ± 46.811  ns/op 
我有一个更复杂的测试(可能需要jmh开发人员进行更严格的审查):

这一次测试完全没有争用时的情况(就像第一次一样),但这一次摆脱了
有偏见的锁定
。结果是:

 Benchmark                       Mode  Cnt   Score   Error  Units
 casVSsynchronized.CASSync.cas   avgt    5  44.003 ± 1.343  ns/op
 casVSsynchronized.CASSync.sync  avgt    5  50.744 ± 1.370  ns/o


我的结论是:对于竞争环境,CAS要好得多。对于其他人来说,这是有争议的

我将此函数添加到您的代码中
public int cas()抛出异常{int i;do{i=atomicInteger.get();}而(!atomicInteger.compareAndSet(i,i+1));返回i+1;}
此函数最差。使用compareAndSet Explicit不是一个好主意?@twogoods我真的不明白你添加了什么以及确切的位置。您是否声明了一个名为
atomicInteger
的新变量?还有为什么要使用while循环呢?compareAndSet和while循环是实现incrementAndGet的一种方法,如果使用AtomicReference,则必须在代码中使用compareAndSet,因此在某些代码场景中使用compareAndSet explicit会更好地匹配。
import java.util.concurrent.TimeUnit;

@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
public class CASSync {

    public static void main(String[] args) throws RunnerException {

        Options opt = new OptionsBuilder()
                .jvmArgs("-ea")
                .shouldFailOnError(true)
                .include(CASSync.class.getSimpleName()).build();
        new Runner(opt).run();
    }

    @State(Scope.Thread)
    static public class AtomicHolder {

        AtomicInteger i = null;

        @Setup(Level.Invocation)
        public void setUp() {
            i = new AtomicInteger(0);
        }

        @TearDown(Level.Invocation)
        public void tearDown() {
            assert i.intValue() == 1;
            i = null;
        }

    }

    @State(Scope.Thread)
    static public class SyncHolder {

        int i = 0;

        Object lock = null;

        @Setup(Level.Invocation)
        public void setUp() {
            lock = new Object();
            i = 0;
        }

        @TearDown(Level.Invocation)
        public void tearDown() {
            assert i == 1;
            lock = null;
        }

    }

    @Benchmark
    @Fork(1)
    public boolean cas(AtomicHolder holder) {
        return holder.i.compareAndSet(0, 1);
    }

    @Benchmark
    @Fork(1)
    public boolean sync(SyncHolder holder) {
        synchronized (holder.lock) {
            ++holder.i;
        }

        return holder.i == 1;
    }

}
 Benchmark                       Mode  Cnt   Score   Error  Units
 casVSsynchronized.CASSync.cas   avgt    5  44.003 ± 1.343  ns/op
 casVSsynchronized.CASSync.sync  avgt    5  50.744 ± 1.370  ns/o