我对多线程增量的最佳性能进行调查。我检查了基于同步,AtomicInteger和自定义实现的实现,如AtomicInteger,但是使用parkNanos(1),在失败的CAS上。
private int customAtomic() {
int ret;
for (;;) {
ret = intValue;
if (unsafe.compareAndSwapInt(this, offsetIntValue, ret, ++ret)) {
break;
}
LockSupport.parkNanos(1);
}
return ret;
}
我基于JMH做了基准:明确执行每个方法,每个方法消耗CPU(1,2,4,8,16次)并且只消耗CPU。每个基准测试方法在1-17线程上在Intel(R)Xeon(R)CPU E5-1680 v2 @ 3.00GHz,8 Core + 8 HT 64Gb RAM上执行。 结果让我感到惊讶:
问题:
我尝试执行此测试几次,并且spike总是发生在不同的数字线程中。另外我在另一台机器上试过这个测试,结果是一样的。也许这是测试中的问题。在"坏情况"在StackProfiler中定制impl,我看到了:
....[Thread state distributions]....................................................................
50.0% RUNNABLE
49.9% TIMED_WAITING
....[Thread state: RUNNABLE]........................................................................
43.3% 86.6% sun.misc.Unsafe.park
5.8% 11.6% com.jad.generated.IncrementBench_incrementCustomAtomicWithWork_jmhTest.incrementCustomAtomicWithWork_thrpt_jmhStub
0.8% 1.7% org.openjdk.jmh.infra.Blackhole.consumeCPU
0.1% 0.1% com.jad.IncrementBench$Worker.work
0.0% 0.0% java.lang.Thread.currentThread
0.0% 0.0% com.jad.generated.IncrementBench_incrementCustomAtomicWithWork_jmhTest._jmh_tryInit_f_benchmarkparams1_0
0.0% 0.0% org.openjdk.jmh.infra.generated.BenchmarkParams_jmhType_B1.<init>
....[Thread state: TIMED_WAITING]...................................................................
49.9% 100.0% sun.misc.Unsafe.park
好的情况&#34;:
....[Thread state distributions]....................................................................
88.2% TIMED_WAITING
11.8% RUNNABLE
....[Thread state: TIMED_WAITING]...................................................................
88.2% 100.0% sun.misc.Unsafe.park
....[Thread state: RUNNABLE]........................................................................
5.6% 47.9% sun.misc.Unsafe.park
3.1% 26.3% org.openjdk.jmh.infra.Blackhole.consumeCPU
2.4% 20.3% com.jad.generated.IncrementBench_incrementCustomAtomicWithWork_jmhTest.incrementCustomAtomicWithWork_thrpt_jmhStub
0.6% 5.5% com.jad.IncrementBench$Worker.work
0.0% 0.0% com.jad.generated.IncrementBench_incrementCustomAtomicWithWork_jmhTest.incrementCustomAtomicWithWork_Throughput
0.0% 0.0% java.lang.Thread.currentThread
0.0% 0.0% org.openjdk.jmh.infra.generated.BenchmarkParams_jmhType_B1.<init>
0.0% 0.0% sun.misc.Unsafe.putObject
0.0% 0.0% org.openjdk.jmh.runner.InfraControlL2.announceWarmdownReady
0.0% 0.0% sun.misc.Unsafe.compareAndSwapInt
Link to result graphs. X - threads count, Y - thpt, op/microsec
UPD
好的,我知道,据我所知,当我使用parkNanos时,一个线程也可以长时间保持锁定(CAS)。 CAS失败的线程进入休眠状态,只有一个线程正在工作并递增值。我看到,对于大并发级别,当工作量很小时 - AtomicInteger不是更好的方法。但是如果我们增加workSize,例如level = CASThrpt / threadNum,它应该可以正常工作: 对于本地机器,我设置了workSize = 300,我的测试结果:
Benchmark (workSize) Mode Cnt Score Error Units
IncrementBench.incrementAtomicWithWork 300 thrpt 3 4.133 ± 0.516 ops/us
IncrementBench.incrementCustomAtomicWithWork 300 thrpt 3 1.883 ± 0.234 ops/us
IncrementBench.lockIntWithWork 300 thrpt 3 3.831 ± 0.501 ops/us
IncrementBench.onlyWithWork 300 thrpt 3 4.339 ± 0.243 ops/us
AtomicInteger - 获胜,锁定 - 第二名,自定义 - 第三名。 但是尖峰问题仍然不明确。我忘记了java版本: Java(TM)SE运行时环境(版本1.7.0_79-b15) Java HotSpot(TM)64位服务器VM(内置24.79-b02,混合模式)
答案 0 :(得分:1)
在同步的情况下,它往往是带锁的粘性,这意味着一个线程可以长时间保持锁定而不让另一个线程公平地抓住它。这对于多线程来说是非常糟糕的,但是如果你有一个基准测试,如果只有一个线程运行相对较长的时间,它会表现得更好。
您需要更改测试,以便在使用多个线程时比使用一个线程更好地运行,或者您实际上将测试哪个锁定策略具有最差的公平策略。
锁定策略试图调整锁定的执行方式,这就是为什么它可以改变行为,但它不能很好地完成,因为代码本来就不应该是多线程的。