CPU缓存行及伪共享

CPU Cache概述

随着CPU的频率不断提升,而内存的访问速度却没有质的突破,为了弥补访问内存的速度慢,充分发挥CPU的计算资源,提高CPU整体吞吐量,在CPU与内存之间引入了一级Cache。随着热点数据体积越来越大,一级Cache L1已经不满足发展的要求,引入了二级Cache L2,三级Cache L3。(注:若无特别说明,本文的Cache指CPU Cache,高速缓存)CPU Cache在存储器层次结构中的示意如下图:


计算机早已进入多核时代,软件也越来越多的支持多核运行。一个处理器对应一个物理插槽,多处理器间通过QPI总线相连。一个处理器包含多个核,一个处理器间的多核共享L3 Cache。一个核包含寄存器、L1 Cache、L2 Cache,下图是Intel Sandy Bridge CPU架构,一个典型的NUMA多处理器结构:

作为程序员,需要理解计算机存储器层次结构,它对应用程序的性能有巨大的影响。如果需要的程序是在CPU寄存器中的,指令执行时1个周期内就能访问到他们。如果在CPU Cache中,需要130个周期;如果在主存中,需要50200个周期;在磁盘上,大概需要几千万个周期。充分利用它的结构和机制,可以有效的提高程序的性能。

以我们常见的X86芯片为例,Cache的结构下图所示:整个Cache被分为S个组,每个组是又由E行个最小的存储单元——Cache Line所组成,而一个Cache Line中有B(B=64)个字节用来存储数据,即每个Cache Line能存储64个字节的数据,每个Cache Line又额外包含一个有效位(valid bit)、t个标记位(tag bit),其中valid bit用来表示该缓存行是否有效;tag bit用来协助寻址,唯一标识存储在CacheLine中的块;而Cache Line里的64个字节其实是对应内存地址中的数据拷贝。根据Cache的结构题,我们可以推算出每一级Cache的大小为B×E×S。

Cache Line伪共享分析

说伪共享前,先看看Cache Line 在java编程中使用的场景。如果CPU访问的内存数据不在Cache中(一级、二级、三级),这就产生了Cache Line miss问题,此时CPU不得不发出新的加载指令,从内存中获取数据。通过前面对Cache存储层次的理解,我们知道一旦CPU要从内存中访问数据就会产生一个较大的时延,程序性能显著降低,所谓远水救不了近火。为此我们不得不提高Cache命中率,也就是充分发挥局部性原理。

局部性包括时间局部性、空间局部性。时间局部性:对于同一数据可能被多次使用,自第一次加载到Cache Line后,后面的访问就可以多次从Cache Line中命中,从而提高读取速度(而不是从下层缓存读取)。空间局部性:一个Cache Line有64字节块,我们可以充分利用一次加载64字节的空间,把程序后续会访问的数据,一次性全部加载进来,从而提高Cache Line命中率(而不是重新去寻址读取)。

看个例子:内存地址是连续的数组(利用空间局部性),能一次被L1缓存加载完成。

如下代码,长度为16的row和column数组,在Cache Line 64字节数据块上内存地址是连续的,能被一次加载到Cache Line中,所以在访问数组时,Cache Line命中率高,性能发挥到极致。

public int run(int[] row, int[] column) {    int sum = 0;    for(int i = 0; i < 16; i++ ) {        sum += row[i] * column[i];    }    return sum;}

而上面例子中变量i则体现了时间局部性,i作为计数器被频繁操作,一直存放在寄存器中,每次从寄存器访问,而不是从主存甚至磁盘访问。虽然连续紧凑的内存分配带来高性能,但并不代表它一直都能带来高性能。如果把它放在多线程中将会发生什么呢?如图:

数据X、Y、Z被加载到同一Cache Line中,线程A在Core1修改X,线程B在Core2上修改Y。根据MESI大法,假设是Core1是第一个发起操作的CPU核,Core1上的L1 Cache Line由S(共享)状态变成M(修改,脏数据)状态,然后告知其他的CPU核,图例则是Core2,引用同一地址的Cache Line已经无效了;当Core2发起写操作时,首先导致Core1将X写回主存,Cache Line状态由M变为I(无效),而后才是Core2从主存重新读取该地址内容,Cache Line状态由I变成E(独占),最后进行修改Y操作, Cache Line从E变成M。可见多个线程操作在同一Cache Line上的不同数据,相互竞争同一Cache Line,导致线程彼此牵制影响,变成了串行程序,降低了并发性。此时我们则需要将共享在多线程间的数据进行隔离,使他们不在同一个Cache Line上,从而提升多线程的性能。



 

Cache Line伪共享处理方案

处理伪共享的两种方式:

  1. 增大数组元素的间隔使得不同线程存取的元素位于不同的cache line上。典型的空间换时间。(Linux cache机制与之相关)
  2. 在每个线程中创建全局数组各个元素的本地拷贝,然后结束后再写回全局数组。

在Java类中,最优化的设计是考虑清楚哪些变量是不变的,哪些是经常变化的,哪些变化是完全相互独立的,哪些属性一起变化。举个例子:


 

public class Data{    long modifyTime;    boolean flag;    long createTime;    char key;    int value;}

假如业务场景中,上述的类满足以下几个特点:

  1. 当value变量改变时,modifyTime肯定会改变
  2. createTime变量和key变量在创建后,就不会再变化。
  3. flag也经常会变化,不过与modifyTime和value变量毫无关联。

当上面的对象需要由多个线程同时的访问时,从Cache角度来说,就会有一些有趣的问题。当我们没有加任何措施时,Data对象所有的变量极有可能被加载在L1缓存的一行Cache Line中。在高并发访问下,会出现这种问题:

如上图所示,每次value变更时,根据MESI协议,对象其他CPU上相关的Cache Line全部被设置为失效。其他的处理器想要访问未变化的数据(key 和 createTime)时,必须从内存中重新拉取数据,增大了数据访问的开销。

JAVA处理方式

Padding 方式

正确的方式应该将该对象属性分组,将一起变化的放在一组,与其他属性无关的属性放到一组,将不变的属性放到一组。这样当每次对象变化时,不会带动所有的属性重新加载缓存,提升了读取效率。在JDK1.8以前,我们一般是在属性间增加长整型变量来分隔每一组属性。被操作的每一组属性占的字节数加上前后填充属性所占的字节数,不小于一个cache line的字节数就可以达到要求:

public class DataPadding{    long a1,a2,a3,a4,a5,a6,a7,a8;//防止与前一个对象产生伪共享    int value;    long modifyTime;    long b1,b2,b3,b4,b5,b6,b7,b8;//防止不相关变量伪共享;    boolean flag;    long c1,c2,c3,c4,c5,c6,c7,c8;//    long createTime;    char key;    long d1,d2,d3,d4,d5,d6,d7,d8;//防止与下一个对象产生伪共享}

通过填充变量,使不相关的变量分开

Contended注解方式

在JDK1.8中,新增了一种注解@sun.misc.Contended,来使各个变量在Cache line中分隔开。注意,jvm需要添加参数-XX:-RestrictContended才能开启此功能
用时,可以在类前或属性前加上此注释:


 

// 类前加上代表整个类的每个变量都会在单独的cache line中@sun.misc.Contended@SuppressWarnings("restriction")
public class ContendedData {int value;long modifyTime;boolean flag;long createTime;char key;
}
或者这种:
// 属性前加上时需要加上组标签@SuppressWarnings("restriction")
public class ContendedGroupData {@sun.misc.Contended("group1") int value;@sun.misc.Contended("group1") long modifyTime;@sun.misc.Contended("group2") boolean flag;@sun.misc.Contended("group3") long createTime;@sun.misc.Contended("group3") char key;
}

采取上述措施图示:

JDK1.8 ConcurrentHashMap的处理

java.util.concurrent.ConcurrentHashMap在这个如雷贯耳的Map中,有一个很基本的操作问题,在并发条件下进行++操作。因为++这个操作并不是原子的,而且在连续的Atomic中,很容易产生伪共享(false sharing)。所以在其内部有专门的数据结构来保存long型的数据:

  /*** A padded cell for distributing counts.  Adapted from LongAdder* and Striped64.  See their internal docs for explanation.*/@sun.misc.Contended static final class CounterCell {volatile long value;CounterCell(long x) { value = x; }}

我们看到该类中,是通过@sun.misc.Contended达到防止false sharing的目的

JDK1.8 Thread 的处理

java.lang.Thread在java中,生成随机数是和线程有着关联。而且在很多情况下,多线程下产生随机数的操作是很常见的,JDK为了确保产生随机数的操作不会产生false sharing ,把产生随机数的三个相关值设为独占cache line。

/** Secondary seed isolated from public ThreadLocalRandom sequence */@sun.misc.Contended("tlr")int threadLocalRandomSecondarySeed;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/770123.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营第三十二天 | 122.买卖股票的最佳时机II ,55. 跳跃游戏 , 45.跳跃游戏II

贪心&#xff1a;只要把每一个上升区间都吃到手&#xff0c;就能一直赚 class Solution { public:int maxProfit(vector<int>& prices) {int res 0;for(int i 1;i< prices.size();i){int diff prices[i] - prices[i-1];if(prices[i] > prices[i-1]){res d…

蓝桥杯练习题总结(二)dfs题、飞机降落、全球变暖

目录 一、飞机降落 二、全球变暖 初始化和输入 确定岛屿 DFS搜索判断岛屿是否会被淹没 计算被淹没的岛屿数量 三、军训排队 一、飞机降落 问题描述&#xff1a; N架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 时刻到达机场上空&#xff0c;到达时它的剩余…

[Linux]文件缓冲区

文件fd 输出重定向除了用dup2()改变数组下标外&#xff0c;还可以用命令来完成 所有的命令执行&#xff0c;都必须有操作系统将其运行起来变成进程&#xff0c;然后根据>>, <<来判断是输入重定向&#xff0c;还是输出重定向。 缓冲区 之所以有缓冲区&#xff0…

JavaScript 基础、内置对象、BOM 和 DOM 常用英文单词总结

一提到编程、软件、代码。对于英语不是很熟悉的同学望而却步。其实没有想像中的难么难&#xff0c;反复练习加上自己的思考、总结&#xff0c;会形成肌肉记忆。整理一下&#xff0c;初学者每天30遍。 1、JavaScript 基础语法 break&#xff1a;中断循环或 switch 语句的执行。…

安卓转鸿蒙竟如此丝滑

随着鸿蒙的爆火&#xff0c;大家都想知道鸿蒙能不能搞&#xff1f; 相信大家搞开发的&#xff0c;都多多少少的了解过鸿蒙。近几个月鸿蒙的大动作也不少&#xff0c;如&#xff1a;重庆市近20个垂域应用与鸿蒙原生合作、深圳制定鸿蒙《行动计划》、阿里再次与鸿蒙展开合作&…

微信小程序开发技巧:canvas实现电子签名

在微信小程序中实现电子签名功能方式很多,本文采用canvas绘制的方式实现。具体实现步骤如下: 在页面中添加canvas元素 <view class"container"><canvas canvas-id"signCanvas" class"canvas" disable-scrolltrue touchstart"sta…

跨时钟域学习记录(一)

亚稳态 亚稳态是电平介于高低电平之间的一种暂时状态。在同步系统中&#xff0c;当寄存器的信号无法满足建立时间和保持时间时&#xff0c;输出端的信号就可能出现亚稳态。在异步系统中&#xff0c;亚稳态并非一定在建立时间和保持时间无法满足时出现。   受噪声、温度、电压…

【vue3.0】实现导出的PDF文件内容是红头文件格式

效果图: 编写文件里面的主要内容 <main><div id"report-box"><p>线索描述</p><p class"label"><span>线索发现时间:</span> <span>{{ detailInfoVal?.problem.createdDate }}</span></p><…

MyBatis3源码深度解析(二十一)动态SQL实现原理(二)动态SQL解析过程、#{}和${}的区别

文章目录 前言8.5 动态SQL解析过程8.5.1 SQL配置转换为SqlSource对象8.5.2 SqlSource转换为静态SQL语句 8.6 #{}和${}的区别8.7 小结 前言 在【MyBatis3源码深度解析(二十)动态SQL实现原理(一)动态SQL的核心组件】中研究了MyBatis动态SQL相关的组件&#xff0c;如SqlSource用于…

AAC相关知识

一、AAC音频格式种类有哪些 AAC音频格式是一种由MPEG-4标准定义的有损音频压缩格式。AAC包含两种封装格式 ADIF&#xff08;Audio Data Interchange Format音频数据交换格式&#xff09;和ADTS&#xff08;Audio Data transport Stream音频数据传输流&#xff09;。 ADIF 特点…

promethus的安装使用

1、# 软件下载地址 https://prometheus.io/download/ https://grafana.com/grafana/download https://prometheus.io/download/ Prometheus是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的。 Prometheus 的优点 1、非常少的外部依赖,安装…

OceanBase4.2.2.1 单机集群在ArmX86安装(自测记录)

OceanBase OceanBase就不必多加介绍了&#xff0c;本次主要是分享对于它的安装使用&#xff0c;先说说背景&#xff0c;首先接触是因为信创国产化的要求&#xff0c;为满足支持国产化&#xff0c;安装了Arm架构下版本4.0.0&#xff0c;满足支持通过。后来项目实际使用&#xff…

由浅到深认识Java语言(20):包装类

该文章Github地址&#xff1a;https://github.com/AntonyCheng/java-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.c…

leetcode刷题日记-外观数组

题目描述 解题思路 初始化字符串 init 为 “1”&#xff0c;作为外观数列的第一项。 通过循环迭代生成外观数列的下一项&#xff0c;循环次数为 n-1&#xff0c;因为已经初始化了第一项。 在每次迭代中&#xff0c;通过两个指针 pos 和 start 来遍历当前项 init&#xff0c;po…

C++中的枚举类型

C中的enum&#xff08;枚举&#xff09;类型是一种用户定义的类型&#xff0c;用于表示一组整数值&#xff0c;每个值都有对应的名称&#xff0c;增强了代码的可读性和可维护性。 1. 基本枚举类型 基本的枚举类型定义了一组命名的整数常量。 enum Color {RED,GREEN,BLUE };C…

创业之路:探索如何在Facebook上创业成功

引言 在当今数字化时代&#xff0c;社交媒体已成为创业者们开展业务、建立品牌和推广产品的重要平台之一。作为全球最大的社交媒体平台之一&#xff0c;Facebook为创业者提供了丰富的机会和资源。本文将探讨如何在Facebook上创业成功的关键因素和实践方法&#xff0c;帮助创业…

第九届蓝桥杯大赛个人赛省赛(软件类)真题C 语言 A 组-第几个幸运数字

幸运数字是可以被3,5,7任一整除的数字&#xff0c;列举小明号码内的所有可能组合并计数。注意别忘了把1占的一位减去。 #include<stdio.h> typedef long long ll; int main(){long long ans 0, n 59084709587505LL;for(ll i 1; i < n; i * 3){//计算小于等于n的数…

计算机程序的编译和链接

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

下载网页上的在线视频 网络视频 视频插件下载

只需要在浏览器上安装一个插件&#xff0c;就可以下载大部分的视频文件&#xff0c;几秒到一两个小时的视频&#xff0c;基本都不是问题。详细解决如下&#xff1a; 0、因为工作需要&#xff0c;需要获取某网站上的宣传视频&#xff0c;我像往常一样&#xff0c;查看视频的url…

Schemdraw小白从入门到放弃---原理工具书

文章目录 序版本最简单的例子一、总体思路二、元件2.1 color习题 2.2 label2.3 length 三、元件的连接3.1 延续性习题 3.2 方向习题 3.3 接线点习题3.3.1 默认激活anchor与沉默anchor3.3.2 切换鼠标焦点机制3.3.2.1 at函数规定元件的start接在哪个anchor上3.3.2.2 to函数规定元…