感谢同事【空蒙】的投稿
之前看了java8的longadder实现,最近又看到一篇文章介绍longadder实现的。其实现思路也是分段,最后需要get的时候,再进行sum计算。其核心思路就是减少并发,但之前老的Atomic,难道就没有提升的空间了吗?昨晚进行了一次测试。测试代码如下:
02 |
* Atomically increments by one the current value. |
04 |
*@return the updated value |
06 |
public final int incrementAndGet() { |
12 |
int next = current + 1 ; |
14 |
if (compareAndSet(current, next)) |
以incrementAndGet为例,在非常高的并发下,compareAndSet会很大概率失败,因此导致了此处cpu不断的自旋,对cpu资源的浪费
既然知道此地是高并发的瓶颈,有什么办法呢?
01 |
public class AtomicBetter { |
03 |
AtomicInteger ai= new AtomicInteger(); |
05 |
public int incrementAndGet() { |
09 |
int current =ai.get(); |
11 |
int next = current + 1 ; |
13 |
if (compareAndSet(current, next)) |
33 |
private boolean compareAndSet(intcurrent,intnext) { |
35 |
if (ai.compareAndSet(current, next)) { |
41 |
LockSupport.parkNanos( 1 ); |
很简单,当cas失败后,对线程park,减少多线程竞争导致的频繁cas失败,更进一步的导致cpu自旋,浪费cpu的运算能力。在4核虚拟机,Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz linux 2.6.32,(注意,老版本的内核,不支持高的精度ns级) 进行测试,同样都起4个线程,每个线程里面对AtomicInteger进行5kw次的incrementAndGet。原生的AtomicInteger,耗时14232ms,进行了35870次上下文切换,总共87967770955次时钟周期。那prak 1ns下呢,耗时5195ms,进行了19779次上下文切换,总共36187480878次时钟周期,明显性能上比原生的AtomicInteger更好,那这个park多少合适呢?那就只有人肉测试了
park |
time(ms) |
context-switches |
cycles |
AtomicInteger |
14232 |
35,870 |
87,967,770,955 |
1ns |
5195 |
19,779 |
36,187,480,878 |
10ns |
5050 |
20,223 |
34,839,351,263 |
100ns |
5238 |
20,724 |
37,250,431,417 |
125ns |
4536 |
47,479 |
26,149,046,788 |
140ns |
4008 |
100,022 |
18,342,728,517 |
150ns |
3864 |
110,720 |
16,146,816,453 |
200ns |
3561 |
125,694 |
11,793,941,243 |
300ns |
3456 |
127,072 |
10,200,338,988 |
500ns |
3410 |
132,163 |
9,545,542,340 |
1us |
3376 |
134,463 |
9,125,973,290 |
5us |
3383 |
122,795 |
9,009,226,315 |
10us |
3367 |
113,930 |
8,905,263,507 |
100us |
3391 |
50,925 |
8,359,532,733 |
500us |
3456 |
17,225 |
8,096,303,146 |
1ms |
3486 |
10,982 |
7,993,812,198 |
10ms |
3456 |
2,600 |
7,845,610,195 |
100ms |
3555 |
1,020 |
7,804,575,756 |
500ms |
3854 |
822 |
7,814,209,077 |
本机环境下,park 1ms下,相对耗时,cs次数来说是最好的。因此这种优化要达到最佳效果,还要看cpu的情况而定,不是一概而定的
两个问题:
1、cas失败对线程park的副作用是什么。
2、如果park的时间继续加大,那会是这么样的结果呢。
文章转自 并发编程网-ifeve.com