Java 如何测试同步数据之间的比较数据集的性能
我想用CAS来改进我的代码,但我怀疑它能获得更好的性能,所以我做了一个测试。这是测试代码,这个jmh代码可靠吗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
@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