简述

本地旨在验证在《深入刨析volatile关键词》中提到的CPU Cache中缓存一致性协议可能会出现的CacheMiss;

缓存行Cache Line

缓存是由缓存行组成的。一般一行缓存行有64字节。CPU在操作缓存时是以缓存行为单位的,可以通过如下命令查看缓存行的大小:

[root@yangsc-01 ~]# cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
64
[root@yangsc-01 ~]#

由于CPU存取缓存都是按行为最小单位操作的。对于long类型来说,一个long类型的数据有64位,也就是8个字节,所以对于数组来说,由于数组中元素的地址是连续的,所以在加载数组中第一个元素的时候会把后面的元素也加载到缓存行中。如果一个long类型的数组长度是8,那么也就是64个字节了,CPU这时操作该数组,似乎应该会把数组中所有的元素都放入缓存行,但是答案却是否定的,原因就是在Java中,对象在内存中的结构包含对象头。在《深入剖析synchronized关键词》一个对象的内存布局小节 有相关描述;

一张经典的Cache Line

一个运行在处理器core 1上的线程想要更新变量X的值, 同时另外一个运行在处理器core 2上的线程想要更新变量Y的值. 但是, 这两个频繁改动的变量都处于同一条缓存行. 两个线程就会轮番发送RFO消息, 占得此缓存行的拥有权. 当core 1取得了拥有权开始更新X, 则core 2对应的缓存行需要设为I状态. 当core 2取得了拥有权开始更新Y, 则core 1对应的缓存行需要设为I状态(失效态). 轮番夺取拥有权不但带来大量的RFO消息, 而且如果某个线程需要读此行数据时, L1和L2缓存上都是失效数据, 只有L3缓存上是同步好的数据;而L3的Cache性能不好;

验证CacehLine存在?

先看结果

  • VolatileLong耗时:31028毫秒

private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];

  • VolatileLong2耗时:7650毫秒

private static VolatileLong2[] longs = new VolatileLong2[NUM_THREADS]; // 7650

  • VolatileLong3耗时:7385毫秒

private static VolatileLong3[] longs = new VolatileLong3[NUM_THREADS]; // 7650

public class FalseSharing implements Runnable {
public final static int NUM_THREADS = 4; // change
public final static long ITERATIONS = 500L * 1000L * 1000L;
private final int arrayIndex;
// private static VolatileLong[] longs = new VolatileLong[NUM_THREADS]; // 31028
private static VolatileLong2[] longs = new VolatileLong2[NUM_THREADS]; // 7650
// private static VolatileLong3[] longs = new VolatileLong3[NUM_THREADS]; // 7385
static {
for (int i = 0; i < longs.length; i++) {
longs[i] = new VolatileLong2();
}
VolatileLong volatileLong = new VolatileLong();
VolatileLong2 volatileLong2 = new VolatileLong2();
VolatileLong3 volatileLong3 = new VolatileLong3(); System.out.println(ClassLayout.parseInstance(volatileLong).toPrintable());
System.out.println(ClassLayout.parseInstance(volatileLong2).toPrintable());
System.out.println(ClassLayout.parseInstance(volatileLong3).toPrintable());
} public FalseSharing(final int arrayIndex) {
this.arrayIndex = arrayIndex;
} public static void main(final String[] args) throws Exception {
long start = System.currentTimeMillis();
runTest();
System.out.println("duration = " + (System.currentTimeMillis() - start));
} private static void runTest() throws InterruptedException {
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new FalseSharing(i));
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
} @Override
public void run() {
long i = ITERATIONS + 1;
while (0 != --i) {
longs[arrayIndex].value = i;
}
} public final static class VolatileLong {
public volatile long value = 0L;
} // long padding避免false sharing
public final static class VolatileLong2 {
volatile long p0, p1, p2, p3, p4, p5, p6;
public volatile long value = 0L;
volatile long q0, q1, q2, q3, q4, q5, q6;
} /**
* jdk8新特性,Contended注解避免false sharing
* 需要加参数运行: -XX:-RestrictContended
*/
@sun.misc.Contended
public final static class VolatileLong3 {
public volatile long value = 0L;
}
}
  • ClassLayout内存布局分析

开启了指针压缩,markword+classporint+padding,VolatileLong占用了24bytes,不满足CacheLine在大多数机器上的64字节的条件,volatile又是线程可见的,不同的线程修改了之后,需要让别的线程看到,在不同的CacheLine

  • ClassLayout2内存布局分析

markword+classporint+padding+(p+q自主)padding占用136bytes,可以分布到不同的CacheLine上;

  • ClassLayout3内存布局分析

markword+classporint+padding+(p+q自主)padding占用280bytes,可以分布到不同的CacheLine上;

com.yangsc.juc.FalseSharing$VolatileLong object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) c1 c1 00 f8 (11000001 11000001 00000000 11111000) (-134168127)
12 4 (alignment/padding gap)
16 8 long VolatileLong.value 0
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total com.yangsc.juc.FalseSharing$VolatileLong2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 47 c1 00 f8 (01000111 11000001 00000000 11111000) (-134168249)
12 4 (alignment/padding gap)
16 8 long VolatileLong2.p0 0
24 8 long VolatileLong2.p1 0
32 8 long VolatileLong2.p2 0
40 8 long VolatileLong2.p3 0
48 8 long VolatileLong2.p4 0
56 8 long VolatileLong2.p5 0
64 8 long VolatileLong2.p6 0
72 8 long VolatileLong2.value 0
80 8 long VolatileLong2.q0 0
88 8 long VolatileLong2.q1 0
96 8 long VolatileLong2.q2 0
104 8 long VolatileLong2.q3 0
112 8 long VolatileLong2.q4 0
120 8 long VolatileLong2.q5 0
128 8 long VolatileLong2.q6 0
Instance size: 136 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total com.yangsc.juc.FalseSharing$VolatileLong3 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 132 (alignment/padding gap)
144 8 long VolatileLong3.value 0
152 128 (loss due to the next object alignment)
Instance size: 280 bytes
Space losses: 132 bytes internal + 128 bytes external = 260 bytes total

参考文档写的比我好,想了解更多,请移步到参考连接文章。

参考

从Java视角理解伪共享(False Sharing)

从Java视角理解CPU缓存(CPU Cache)

理解CPU-Cache


你的鼓励也是我创作的动力

打赏地址

温故知新-多线程-Cache Line存在验证的更多相关文章

  1. 温故知新-多线程-深入刨析volatile关键词

    文章目录 摘要 volatile的作用 volatile如何解决线程可见? CPU Cache CPU Cache & 主内存 缓存一致性协议 volatile如何解决指令重排序? volat ...

  2. <转>科普CPU Cache line

    转载于http://coolshell.cn/articles/10249.html CPU cache一直是理解计算机体系架构的重要知识点,也是并发编程设计中的技术难点,而且相关参考资料如同过江之鲫 ...

  3. cpu性能探究 :cache line 原理

     參考: 一个解说Direct Mapped Cache很深入浅出的文章: http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Memory/dir ...

  4. cache line 伪共享

    https://blog.csdn.net/qq_27680317/article/details/78486220认识CPU Cache CPU Cache概述 随着CPU的频率不断提升,而内存的访 ...

  5. Cache Line 伪共享发现与优化

    https://yq.aliyun.com/articles/465504 Cache Line 伪共享发现与优化 作者:吴一昊,杨勇 1. 关于本文 本文基于 Joe Mario 的一篇博客 改编而 ...

  6. 伪共享(False Sharing)和缓存行(Cache Line)

    转载:https://www.jianshu.com/p/a9b1d32403ea https://www.toutiao.com/a6644375612146319886/ 前言 在上篇介绍Long ...

  7. adjacent cache line prefetch

    adjacent cache line prefetch 预读取邻近的缓存数据. 计算机在读取数据时,会智能的认为要读取的数据旁边或邻近的数据也是需要的, 那么其在处理的时候就会将这些邻近的数据预先读 ...

  8. 小师妹学JVM之:cache line对代码性能的影响

    目录 简介 一个奇怪的现象 两个问题的答案 CPU cache line inc 和 add 总结 简介 读万卷书不如行万里路,讲了这么多assembly和JVM的原理与优化,今天我们来点不一样的实战 ...

  9. 程序与CPU,内核,寄存器,缓存,RAM,ROM、总线、Cache line缓存行的作用和他们之间的联系?

    目录 缓存 什么是缓存 L1.L2.L3 为什么要设置那么多缓存.缓存在cup内还是cup外 MESI协议----主流的处理缓存和主存数据不一样问题 Cache line是什么已经 对编程中数组的影响 ...

随机推荐

  1. Python 的十万个为什么?

    随着 Python 在近些年的火爆,网上出现了很多这个方向的公众号和博客,文章也层出不穷. 受到此风气的影响,我也把自己"培养"成了一名技术博主,写作近两年来,陆陆续续写过不少的系 ...

  2. 手机APP自动化环境搭建

    1 摘要 近年来,随着移动应用从数量上和逻辑复杂程度上的增长,以及产品发布周期的紧缩,使得回归测试迫在眉睫,鉴于此APP自动化测试变得越来流行,当前主流的APP自动化工具有:Appium.Roboti ...

  3. Redux:with React(一)

    作者数次强调,redux和React没有关系(明明当初就是为了管理react的state才弄出来的吧),它可以和其他插件如 Angular, Ember, jQuery一起使用.好啦好啦知道啦.Red ...

  4. Redux:Reducers

    action只是描述了“发生了什么事情(导致state需要更新)”,但并不直接参与更新state的操作.state的更新由reducer函数执行. 其基本模式是:(state,action)=> ...

  5. 编译安装路由器用的Privoxy 3.0.28(华硕RT-AC88U,原版梅林384.15)

    编译安装路由器用的Privoxy 3.0.28(华硕RT-AC88U,原版梅林384.15) Privoxy有什么特殊之处? 支持和SOCKS/HTTP代理的级联.这个功能轻松将SOCKS转为HTTP ...

  6. 同一父进程下的子进程之间的通信(pipe通信)

    首先对于fork命令  通过fork命令创建进程 父进程返回子进程id 子进程返回0 失败返回-1 对于pipe通讯机制   pipe通讯是半双工的 也就是说只能一方读一方写 题目中想要P1的输出作为 ...

  7. MySQL的CHAR 和 VARCHAR的区别

    CHAR 和 VARCHAR 类型,CHAR 列的长度固定, VARCHAR 列中的值为可变长字符串.在检索的时候,CHAR 列删除了尾部的空格,而 VARCHAR 则保留这些空格s

  8. 写一个LRU算法的记录

    今天简单记录一下,利用Scala解答的一道LRU题目,原题为LeetCode的第146题,是一道设计LRU的题目. 题目详情 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机 ...

  9. 关于Java Web结构和SSM框架的理解

    Java Web常见的三层结构 表现层:也就是Web层,常见的框架有Spring MVC.Struts2 ,并包括用于展示的界面,如JSP界面:业务层:Service层,专注于业务逻辑的实现:持久层: ...

  10. html5学习之路_005

    PHP环境搭建 1.下载安装xampp 2.打开xampp,开启mysql和apache 3.在开发环境eclips中下载插件 4.安装php 5.切换到php开发环境 6.创建一个php项目 7.打 ...