java.util.concurrent.atomic.LongAdder类的可行性
java.util.concurrent.atomic.LongAdder类的可行性,java,concurrency,atomic,java.util.concurrent,Java,Concurrency,Atomic,Java.util.concurrent,LongAdder是一种设计巧妙的原子计数器,它应该能够在更新共享计数器时减少缓存线争用。问题是,它依赖原子CAS操作来实际更新计数(这是它与更通用的longcummerator和friends共享的特性) 在amd64平台(可能还有其他平台)上,atomic CAS比atomic ADD慢得多,至少在涉及到中等数量的内核时是如此。因此,似乎在单个共享的VarHandle上简单地进行原子加法是一个更好的主意:代码更简单,而在一个通用(大约8个内核)容器上的性能也不差甚至更好 与VarHandle
LongAdder
是一种设计巧妙的原子计数器,它应该能够在更新共享计数器时减少缓存线争用。问题是,它依赖原子CAS操作来实际更新计数(这是它与更通用的longcummerator
和friends共享的特性)
在amd64平台(可能还有其他平台)上,atomic CAS比atomic ADD慢得多,至少在涉及到中等数量的内核时是如此。因此,似乎在单个共享的VarHandle
上简单地进行原子加法是一个更好的主意:代码更简单,而在一个通用(大约8个内核)容器上的性能也不差甚至更好
与VarHandle
相比,使用LongAdder
是否有任何好处
要了解更多上下文,请执行以下操作:
LongAdder
,作为一个分片CAS循环,肯定比任何简单的CAS循环都快,即使在低争用级别下也是如此LongAdder
default)似乎都比AMD64上的实际硬件原子加法(“LOCK XADD”指令)慢一些。通过大量增加碎片的数量(至少2倍的CPU数量,在大倍数(如8倍或更高)的情况下可以实现额外的增益),可以加快速度LongAdder
被设计为在写操作繁重的场景中比AtomicLong
在任何级别的争用(除了无争用)都能更好地扩展,事实上它确实做到了
如果您的场景不是写得很重,那么AtomicLong
会更快
C2编译器总是用硬编码程序集替换带注释的方法,而不仅仅是在感觉需要这样做的时候
lock xadd
比CAS重试循环更快地增加值LongAdder
比lock xadd
快,因为它在线程之间分割计数器。它创建了多个计数器,因此减少了争用。LongAdder
被设计为在除无争用之外的任何争用级别上,在写操作繁重的情况下都能比AtomicLong更好地扩展,事实上它确实做到了
如果您的场景不是写得很重,那么AtomicLong
会更快
C2编译器总是用硬编码程序集替换带注释的方法,而不仅仅是在感觉需要这样做的时候
lock xadd
比CAS重试循环更快地增加值LongAdder
比lock xadd
快,因为它在线程之间分割计数器。它创建了多个计数器,因此减少了争用。毫无疑问,LongAdder
比简单的CAS循环快(正如您可能知道的,JVM默认情况下作为CAS执行所有原子操作,并且可能有选择地提升某些CAS使用以“锁定XADD”)。然而,在我看来,AMD64上的“LOCK XADD”总是比分片CA更快,无论争用级别如何。@oakad使用JMH基准来证明这一点是微不足道的。我在长加法器上观察到535M/s,在8核+8线程运行上观察到47M/s与XADD。毫无疑问,LongAdder
比简单的CAS循环快(正如您可能知道的,JVM默认情况下作为CAS执行所有原子操作,并且可能有选择地促进某些CAS使用以“锁定XADD”)。然而,在我看来,AMD64上的“LOCK XADD”总是比分片CA更快,无论争用级别如何。@oakad使用JMH基准来证明这一点是微不足道的。我在LongAdder上观察到535M/s,在8核+8线程运行上观察到XADD的速度为47M/s。很可能你的基准测试有缺陷。请分享。你的基准可能有缺陷。请分享。