从LongAdder说到内存伪共享

LWIP协议栈在STM32H7上的内存优化实战:如何用32KB RAM实现稳定千兆以太网通信 本文深入探讨了在STM32H7微控制器上,如何通过内存优化策略,在仅32KB的D2 SRAM中稳定运行LWIP协议栈以实现千兆以太网通信。文章详细分析了STM32H7的内存架构、LWIP的内存消耗瓶颈,并提供了从描述符表与PBUF池精确分配、链接脚本修改到MPU区域划分的一整套实战优化方案,旨在解决大数据量传输卡死等常见配置问题。 阅读详情

简介

LongAdder 为AtomicLong增强版

架构图

特点:

1、AtomicLong 多线程CAS竞争同一个锁,其他自旋,LongAdder 多个Cell对象多个锁,获得锁失败,会尝试在别的cell上进行竞争,减少锁冲突,适合写多读少
2、LongAdder 中的 cell数组长度初始0,随后扩容会保证长度与CPU的核数差不多,长度是2的倍数保证hash的均匀
3、cell数组解决了伪共享问题

主角:伪共享

什么是伪共享(概念)

缓存系统中是以缓存行,64字节(cache line)为单位存储的,当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。

什么是缓存行

缓存行通常是 64 字节,并且它有效地引用主内存中的一块地址。一个 Java 的 long 类型是 8 字节,因此在一个缓存行中可以存 8 个 long 类型的变量。所以,如果你访问一个 long 数组,当数组中的一个值被加载到缓存中,它会额外加载另外 7 个,以致你能非常快地遍历这个数组。事实上,你可以非常快速的遍历在连续的内存块中分配的任意数据结构。而如果你在数据结构中的项在内存中不是彼此相邻的(如链表),你将得不到免费缓存加载所带来的优势,并且在这些数据结构中的每一个项都可能会出现缓存未命中。

先说CPU缓存:

· CPU 和主内存之间有好几层缓存,因为即使直接访问主内存也是非常慢的。如果你正在多次对一块数据做相同的运算,那么在执行运算的时候把它加载到离 CPU 很近的地方就有意义了。
· 按照数据读取顺序和与 CPU 结合的紧密程度,CPU 缓存可以分为一级缓存,二级缓存,部分高端 CPU 还具有三级缓存。每一级缓存中所储存的全部数据都是下一级缓存的一部分,越靠近 CPU 的缓存越快也越小。所以 L1 缓存很小但很快,并且紧靠着在使用它的 CPU 内核。L2 大一些,也慢一些,并且仍然只能被一个单独的 CPU 核使用。L3 在现代多核机器中更普遍,仍然更大,更慢,并且被单个插槽上的所有 CPU 核共享。最后,你拥有一块主存,由全部插槽上的所有 CPU 核共享。拥有三级缓存的的 CPU,到三级缓存时能够达到 95% 的命中率,只有不到 5% 的数据需要从内存中查询。
· CPU 执行运算的时候,它先去 L1 查找所需的数据,再去 L2,然后是 L3,最后如果这些缓存中都没有,所需的数据就要去主内存拿。走得越远,运算耗费的时间就越长。所以如果你在做一些很频繁的事,你要确保数据在 L1 缓存中。
缓存模型

多级缓存的数据:

MESI协议及RFO请求

MESI是四种状态:
M(修改,Modified):本地处理器已经修改缓存行,即是脏行,它的内容与内存中的内容不一样,并且此 cache 只有本地一个拷贝(专有);
E(专有,Exclusive):缓存行内容和内存中的一样,而且其它处理器都没有这行数据;
S(共享,Shared):缓存行内容和内存中的一样, 有可能其它处理器也存在此缓存行的拷贝;
I(无效,Invalid):缓存行失效, 不能使用。
RFO请求:

  1. 线程的工作从一个处理器移到另一个处理器, 它操作的所有缓存行都需要移到新的处理器上。此后如果再写缓存行,则此缓存行在不同核上有多个拷贝,需要发送 RFO 请求了。
  2. 两个不同的处理器确实都需要操作相同的缓存行

每个核都有自己私有的 L1,L2 缓存。那么多线程编程时, 另外一个核的线程想要访问当前核内 L1、L2 缓存行的数据, 该怎么办呢?
有人说可以通过第 2 个核直接访问第 1 个核的缓存行,这是当然是可行的,但这种方法不够快。跨核访问需要通过 Memory Controller(内存控制器,是计算机系统内部控制内存并且通过内存控制器使内存与 CPU 之间交换数据的重要组成部分),典型的情况是第 2 个核经常访问第 1 个核的这条数据,那么每次都有跨核的消耗.。更糟的情况是,有可能第 2 个核与第 1 个核不在一个插槽内,况且 Memory Controller 的总线带宽是有限的,扛不住这么多数据传输。所以,CPU 设计者们更偏向于另一种办法: 如果第 2 个核需要这份数据,由第 1 个核直接把数据内容发过去,数据只需要传一次。

什么是伪共享(原理)

配图说明
1、一个运行在处理器 core1上的线程想要更新变量 X 的值,同时另外一个运行在处理器 core2 上的线程想要更新变量 Y 的值。但是,这两个频繁改动的变量都处于同一条缓存行。两个线程就会轮番发送 RFO 消息,占得此缓存行的拥有权。当 core1 取得了拥有权开始更新 X,则 core2 对应的缓存行需要设为 I 状态。当 core2 取得了拥有权开始更新 Y,则 core1 对应的缓存行需要设为 I 状态(失效态)。轮番夺取拥有权不但带来大量的 RFO 消息,而且如果某个线程需要读此行数据时,L1 和 L2 缓存上都是失效数据,只有 L3 缓存上是同步好的数据。从前一篇我们知道,读 L3 的数据非常影响性能。更坏的情况是跨槽读取,L3 都要 miss,只能从内存上加载。
2、表面上 X 和 Y 都是被独立线程操作的,而且两操作之间也没有任何关系。只不过它们共享了一个缓存行,但所有竞争冲突都是来源于共享。

解决方案

1、其中一个解决思路,就是让不同线程操作的对象处于不同的缓存行即可。(手动进行缓冲行填充,一个缓冲行64字节,一个对象的对象头8字节)
伪共享在多核编程中很容易发生,而且非常隐蔽。例如,在 JDK 的 LinkedBlockingQueue 中,存在指向队列头的引用 head 和指向队列尾的引用 tail 。而这种队列经常在异步编程中使有,这两个引用的值经常的被不同的线程修改,但它们却很可能在同一个缓存行,于是就产生了伪共享。线程越多,核越多,对性能产生的负面效果就越大。
2、@sun.misc.Contended 注解在数组中的对象,进行字节填充,这防止了 数组中多个元素共享一个缓存行,在性能上是一个提升

总结

首先就是多次强调的,伪共享是很隐蔽的,我们暂时无法从系统层面上通过工具来探测伪共享事件。其次,不同类型的计算机具有不同的微架构(如 32 位系统和 64 位系统的 java 对象所占自己数就不一样),如果设计到跨平台的设计,那就更难以把握了,一个确切的填充方案只适用于一个特定的操作系统。还有,缓存的资源是有限的,如果填充会浪费珍贵的 cache 资源,并不适合大范围应用。最后,目前主流的 Intel 微架构 CPU 的 L1 缓存,已能够达到 80% 以上的命中率。
综上所述,并不是每个系统都适合花大量精力去解决潜在的伪共享问题。

课外扩展

美团大佬相关场景与分析

青龙人形机器人硬件平台设计全解析:从构型到控制系统的实战指南 本文全面解析了青龙人形机器人硬件平台的设计与实现,涵盖构型设计、驱动系统、能源方案及控制系统等核心模块。重点介绍了43个自由度的仿生学实践、关节模组的技术突破以及多模态感知融合等创新设计,为开发者提供了从理论到实战的完整指南。 阅读详情

相关推荐

Elasticsearch连接中断:深入解析Connection reset by peer问题及优化策略

本文深入解析Elasticsearch连接中断问题,特别是Connection reset by peer错误的成因与解决方案。从TCP层的心跳机制到客户端与服务端的配置差异,详细介绍了网络不稳定、防火墙拦截等常见原因,并提供了六种优化策略,包括KeepAlive调整、TCP参数调优和智能重试机制等,帮助提升Elasticsearch连接的稳定性。

weixin_42546392的博客 410

disruptor (史上最全之1):伪共享原理&性能对比实战

提前说明: 翻译 有瑕疵, 伪共享(False Sharing), 应该翻译为 “错共享”, 才更准确CPU的缓存系统是以缓存行(cache line)为单位存储的,一般的大小为64bytes。在多线程程序的执行过程中,存在着一种情况,多个需要频繁修改的变量存在同一个缓存行当中。假设:有两个线程分别访问并修改X和Y这两个变量,X和Y恰好在同一个缓存行上,这两个线程分别在不同的CPU上执行。

架构师尼恩 1816

行政数据分析大屏实战:用Tableau从Excel到看板的完整搭建流程(含数据连接与字段计算避坑)

本文详细介绍了如何利用Tableau从Excel数据源搭建行政数据分析大屏的完整流程,涵盖数据准备、连接技巧、计算字段优化、看板设计及动态交互部署。通过五阶工作流,帮助用户高效处理多源异构数据,实现业务洞察与可视化呈现,提升行政数据分析效率。

weixin_29696711的博客 337

高并发下解决AtomicLong性能瓶颈的方案——LongAdder

LongAdder类是JDK1.8新增的一个原子性操作类。上一节说到,AtomicLong通过CAS提供了非阻塞的原子性操作,相比用阻塞算法的synchronized来说性能已经得到了很大提升。在高并发下大量线程会同时竞争更新同一个原子变量,但由于只有一个线程的CAS操作会成功,这就造成了大量线程竞争失败后,会通过无限循环不断进行自旋尝试CAS操作,这会白白浪费CPU资源。为了解决AtomicLong在高并发下的缺点,LongAdder应运而生。

BASK2311的博客 375

opencv简单小项目

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库,它提供了大量的图像和视频处理功能。要开始一个OpenCV项目,你需要安装OpenCV库,这通常可以通过Python的pip包管理器完成。替换为你要加载的图像的实际路径。这个例子展示了OpenCV项目的基本结构,你可以在此基础上添加更多的图像处理功能。如果你需要更详细的指导或代码示例,请告诉我你的具体需求。

youyouxiong的博客 971

模拟IC版图DRC实战:手把手教你搞定MIM电容天线错误和ESD.10g违例

本文深入解析模拟IC版图中的MIM电容天线错误和ESD.10g违例问题,提供从错误解码到实战修复的完整指南。通过电路图修改、版图优化和验证闭环等步骤,帮助工程师高效解决DRC错误,特别适用于ADC等精密模拟电路设计。文章还分享了高级排错技巧和预防性设计策略,大幅提升流片成功率。

weixin_27697385的博客 293

LongAdder讲解

思维导图.png 阿里巴巴开发手册.png 对于Java项目中计数统计的一些需求,如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数) 在大多数项目及开源组件中,计数统计使用最多的仍然还是AtomicLong,虽然是阿里巴巴这样说,但是我们仍然要根据使用场景来决定是否使用LongAdder。 今天主要是来讲讲LongAdder的实现原理,还是老方式,通过图文一步步解开LongAdder神秘的面纱,通过此篇文章你会了解到: ...

JMU_software19的博客 2020

跟着团子学SAP PS:项目计划成本与项目预算设计思路

​在做SAP PS模块概览培训的时候,很多时候都会用到下面的一页PPT片子讲到项目计划成本与预算的关系,但真正这幅片子想表达的具体内容甚至大部分PS顾问也是无法阐明清楚的。 而SAP PS的设计其实是在丰富的项目管理学的基础上而开展的,所以今天从业务上结合SAP中的功能点讲一下项目成本计划及预算的逻辑。 在项目管理的十大知识领域中,项目成本管理主要是包括为使项目在批准的预算内完成而对成本进行规划...

Lucas1211的博客 3万+

JAVA中的伪共享与缓存行

JAVA中的伪共享与缓存行

qq_45443475的博客 1870

CANape快速上手

文章帮助记录CANape学习过程。

小弱鸡的博客 1万+

第3.4章 机器人第三方库实战总结(四):G2O优化库从入门到实战(附SLAM实例代码)

自定义边同样是满足复杂应用需求的重要手段。自定义边时,需要明确边所连接的顶点类型以及边所表示的误差项的计算方式。假设我们要定义一条边,用于约束两个上述自定义顶点之间的相对位姿和传感器偏移量关系。我们可以这样定义边类:public:// 计算误差// 计算雅可比矩阵// 读盘和存盘函数,这里暂不实现。

行知SLAM 358

伪共享问题即解决方式

由于缓存读取的机制,不是一次只读一行文字,而是一次读取一个CacheLine的数据(64位的CPU的CacheLine大小为64字节)。当两个无关的但经常被访问的数据同时存在于一个CacheLine中时,就会引发伪共享的问题。可以看到,x和y完全没有任何关系,但是更新x的时候会把其它包含x的缓存行失效,同时y也就失效了,运行这段程序输出的时间为3890ms。通过宏定义,将b强行放在a的下一行CacheLine中,这样,一次性就无法同时读取ab。宏定义,是用于解决伪共享的问题。应用层面(Java版)

kyrieyu111的博客 770

手把手教你使用YOLOv11完成人脸分类识别,YOLOv11人脸识别,解决中文乱码问题,全网最详细教程

手把手教你使用YOLOv11完成人脸分类识别,YOLOv11人脸识别,解决中文乱码问题,全网最详细教程

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 4443

原子累加器 LongAdder

小故事:中午吃饭的时候,大哥们聊到了LongAdder 这个类的实现问题,当时我也有点忘记了具体源码实现,只记得是多线程下提高cpu的利用率,减少自旋时间。利用了分片锁,进行累加的操作!但是大哥们聊到了内存层面!我又回来复习了一下源码! CAS (Compare and Swap) compareAndSet:内存位置 V、旧的预期值 A 和新的值 B cas和volatile可以实现无锁并发,适用于线程数少,多核cpu的场景

代码不会敲的博客 1045

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

原子累加器:LongAdder原理(伪共享原理)

使用LongAdder进行累加比使用AtomicLong性能更好的原因是: Long Adder在有竞争时,设置多个累加单元,Thread-0累加Cell[0]而Thread-1累加Cell[1]…最后将结果汇总,这样他们在累操作加时操作的是不同的Cell变量,因此减少了CAS重试失败,因此性能更高 LongAdder类有几个关键域: //累加单元数组,懒惰初始化 transient volatile Cell[] cells; //基础值(如果没有竞争,则用cas累加这个域) transient vola

weixin_44173793的博客 767

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

LongAdder原理分析

LongAdder由来 LongAdder类是JDK1.8新增的一个原子性操作类。AtomicLong通过CAS算法提供了非阻塞的原子性操作,相比受用阻塞算法的同步器来说性能已经很好了,但是JDK开发组并不满足于此,因为非常搞并发的请求下AtomicLong的性能是不能让人接受的。 如下AtomicLong 的incrementAndGet的代码,虽然AtomicLong使用CAS算法,但是CA...

大佬请赐教 1万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Sentinel 底层计数组件 LongAdder 源码解析:从 base + cells 拆分计数到缓存行伪共享优化

**导读**:在 Sentinel 的高频限流统计场景中,QPS 等指标需要被大量并发线程持续更新,此时计数器的性能直接决定统计开销。本文以 [Sentinel 底层 LongAdder 的计数实现](https://link.gitcode.com/i/a93c9f9aa7c4eff1bc25ab3bb6786689) 为核心,从源码层面剖析 JDK `java.util.concurrent.

gitblog_00370的博客 244

LongAdder原理

前面介绍了Java中的一些原子类,但是基本都是通过CAS来实现原子性操作,白白浪费CPU资源。JDK8中新增了一个原子性递增或者递减类LongAdder用来克服高并发下使用AtomicLong的缺点。LongAdder的思路是把一个变量分解为多个变量,让同样多的线程去竞争多个资源。如图所示: 使用LongAdder时,内部维护了多个Cell变量,每个Cell里面有一个初始值为0的long型变量,...

reachwang的博客 4277

并发编程(五)锁优化+LongAdder+伪共享+缓存行+CAS+XADD

前提知识了解: 1. 什么是伪共享 CPU缓存系统中是以缓存行(cache line)为单位存储的。目前主流的CPU Cache的Cache Line大小都是64Bytes。在多线程情况下,如果需要修改“共享同一个缓存行的变量”,就会无意中影响彼此的性能,这就是伪共享(False Sharing)。 2. 缓存行 由于共享变量在CPU缓存中的存储是以缓存行为单位,一个缓存行可以存储多个变量(存满当...

小诚信驿站 1655

面试官:什么是伪共享?

大家好,我是石头哥。今天分享的这个问题来自一个朋友面试字节时碰到的,最后他也成功拿到了字节offer。这个问题我想可能挺多人不太清楚,所以想拿出来单独说一说。(类似的,我之前面试也还被问到...

程序猿石头的博客 373

volatile是否存在伪共享问题?

1.什么是伪共享和共享? 在探讨LongAdder是如何解决伪共享问题之前,我们要先梳理清一个概念,什么是伪共享和共享? 共享在Java编程里面我们可以这样理解,有一个Share类,它有一个value的属性。如下: public class Share{ int value; } 我们初始化Share的一个实例,然后启动多个线程去操作它的value属性,此时的Share变量被多个线程操作的这种情况我们称之为共享。 大家都知道...

IT蜗牛的专栏 640

真实字节二面:什么是伪共享?

这个问题来自最近一个朋友字节面试碰到的,最后他也成功拿到了字节offer,这个问题我想可能挺多人不太清楚,所以想拿出来单独说一说。 好了,让我们进入正题。 什么是伪共享 首先大家都知道,随着CPU和内存的发展速度差异的问题,导致CPU的速度远远快于内存,所以一般现在的CPU都加入了高速缓存,就是常说的解决不同硬件之间的性能差异问题。 这样的话,很简单的道理,加入了缓存,就必然会导致缓存一致性的问题,由此,又引入了缓存一致性协议。(如果你不知道,建议去百度一下,这里不做展开) CPU缓存,顾名思义,越贴近CP

艾小仙的专栏 467

缓存行 伪共享

public class LongAdder extends Striped64 implements Serializable { private static final long serialVersionUID = 7249069246863182397L; public LongAdder() { } public void add(long x) { Cell[] as; long b, v; int m; Cell a; /.

beOkWithAnything 312

什么是伪共享

问题 (1)什么是 CPU 缓存行? (2)什么是内存屏障? (3)什么是伪共享? (4)如何避免伪共享? CPU缓存架构 CPU 是计算机的心脏,所有运算和程序最终都要由它来执行。 主内存(RAM)是数据存放的地方,CPU 和主内存之间有好几级缓存,因为即使直接访问主内存也是非常慢的。 如果对一块数据做相同的运算多次,那么在执行运算的时候把它加载到离 CPU 很近的地方就有意义了,比如一个循环计数,你不想每次循环都跑到主内存去取这个数据来增长它吧。 越靠近 CPU 的缓存越快也越小。

每天叫醒自己的不是闹铃 是娟 是梦 是理想。 370

longAdder源码解析

LongAdder 是并发大师 @author Doug Lea (大哥李)的作品,设计的非常精巧LongAdder 类有几个关键域// 累加单元数组, 懒惰初始化 transient volatile Cell [ ] cells;// 基础值, 如果没有竞争, 则用 cas 累加这个域 transient volatile long base;// 在 cells 创建或扩容时, 置为 1, 表示加锁 transient volatile int cellsBusy;无竞争状态。...

兀坐晴窗独饮茶 392

解决伪共享

文章目录1.原理之伪共享2.原子累加器LongAdder increment()方法解读 1.原理之伪共享 其中Cell 即为累加单元类 //防止缓存行伪共享 /** 一个缓存行加入了多个Cell对象叫做伪共享 */ //防止缓存行存入多个Cell对象. @sun.misc.Contended static final class Cell{ volatile long value; Cell(long x) {value =x;} //最重要的方法,用来cas 方式进行累加, prev 表示旧值

qq_44765568的博客 388
上一篇: 数据收集处理引擎Logstash (基础架构图)
下一篇: 布隆过滤器原理与场景
Kindle_code
博客等级 码龄12年 21粉丝 66原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值