当前位置:网站首页>缓存--伪共享问题
缓存--伪共享问题
2022-08-03 11:10:00 【zpv2jdfc】
接上一篇 CPU缓存一致性问题
什么是伪共享
伪共享问题指两个线程A和B,他们俩写入同一个cache block的不同变量时,会导致另一个cpu核心的缓存失效的问题。我们来详细看一下伪共享问题到底是怎么产生的:
假设A线程要访问变量A,B线程要访问变量B,并且变量A和B会被分配到同一个cache line中:
接下来,A线程要读取变量A。此时A、B所在的cache line被加载到核心1的cache中,并且状态被标记为独占:
此时,B线程要访问变量B,那么这个cache line被加载到核心2的cache中。并且两个核心的cache line都标记为共享:
接下来,问题来了。假设A线程修改了变量A,为了保证数据一致性,就需要把核心2的cache line标记为失效:
这样一来,如果B要读取变量B的值,就需要A先将cache line写回内存,然后B再从内存中读取。也就是说,明明B变量自始至终都没有改变过,但是在访问时却需要重新从内存读取。如果A、B两个线程轮流修改变量A、B的话,伪共享问题会严重影响性能。
解决伪共享的办法
1.字节填充
对上面这种情况,如果A、B不被分配在同一个cache line中自然就不存在伪共享问题了。
如何让A、B分配在不同cache line中呢?我们可以通过这个命令查看cache line的大小
more /sys/devices/system/cpu/cpu1/cache/index0/coherency_line_size

可以看到cache line大小为64字节。接下来,我们来对比一下使用字节填充后程序性能会提高多少。
不使用字节填充:
public class Main {
public static void main(String[] args) throws InterruptedException {
Pair pair=new Pair();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 0x7fff_ffff; i++) {
pair.x++;
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 0x7fff_ffff; i++) {
pair.y++;
}
});
long start = System.currentTimeMillis();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(System.currentTimeMillis() - start);
}
}
class Pair{
// long x1,x2,x3,x4,x5,x6,x7;
volatile long x=0;
// long y1,y2,y3,y4,y5,y6,y7;
volatile long y=0;
}
63615
使用字节填充:
public class Main {
public static void main(String[] args) throws InterruptedException {
Pair pair=new Pair();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 0x7fff_ffff; i++) {
pair.x++;
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 0x7fff_ffff; i++) {
pair.y++;
}
});
long start = System.currentTimeMillis();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(System.currentTimeMillis() - start);
}
}
class Pair{
long x1,x2,x3,x4,x5,x6,x7;
volatile long x=0;
long y1,y2,y3,y4,y5,y6,y7;
volatile long y=0;
}
10781
注意这里要用volatile关键字防止编译器优化指令。
2.使用@Contented 注解
@Contented注解原理也是字节填充,注解既可以加在字段,也可以加在类上。加在字段上表示这个字段单独占一个缓存行,加在类上表示类中所有字段都独占一个缓存行。
使用@Contented注解需要配置jvm参数 -XX:-RestrictContended,通过 -XX:ContendedPaddingWidth 可以修改填充的字节大小,有效值范围0 - 8192,默认是128字节。
边栏推荐
- Redis发布订阅和数据类型
- 【一起学Rust】Rust包管理工具Cargo初步了解
- thymeleaf中的日期格式转化
- What is the ERC20 token standard?
- 下午见!2022京东云数据库新品发布会
- 【AppCube】数字孪生万物可视 | 联接现实世界与数字空间
- 【冒泡排序以及奇数偶数排列】
- SAP 电商云 Spartacus UI 的 External Routes 设计明细
- Traceback (most recent call last): File
- Depth study of 100 cases - convolution neural network (CNN) to realize the clothing image classification
猜你喜欢

【Star项目】小帽飞机大战(九)

如何检索IDC研究报告?

Dry goods!A highly structured and sparse linear transformation called Deformable Butterfly (DeBut)

嵌入式软件组件经典架构与存储器分类

MySQL数据库实战(1)

The way of programmer architecture practice: how to design a sustainable evolution system architecture?

【MySQL功法】第2话 · 数据库与数据表的基本操作

完全背包问题的思路解析

干货!一种被称为Deformable Butterfly(DeBut)的高度结构化且稀疏的线性变换
【一起学Rust 基础篇】Rust基础——变量和数据类型
随机推荐
「全球数字经济大会」登陆 N 世界,融云提供通信云服务支持
FR9811S6 SOT-23-6 23V,2A同步降压DC/DC转换器
Realize 2d characters move left and right while jumping
How to make self-introduction
RICON:NER SOTA 又来!
Binary search tree (search binary tree) simulation implementation (there is a recursive version)
【一起学Rust】Rust包管理工具Cargo初步了解
Spinner文字显示不全解决办法
袋鼠云思枢:数驹 DTengine,助力企业构建高效的流批一体数据湖计算平台
[Output each bit of an integer, from high to low.With and without recursion]
代码分析Objective-C中的深拷贝与浅拷贝
[Star Project] Little Hat Plane Battle (9)
【一起学Rust 基础篇】Rust基础——变量和数据类型
Machine Learning (Chapter 1) - Feature Engineering
【Mysql】清理binlog日志的方法
【输出一个整数的的每一位,由高到低输出。使用递归和不使用递归】
【多线程的相关内容】
LP流动性挖矿DAPP系统开发丨流动性挖矿功能原理及说明
MySQL数据库实战(1)
ABAB-740新语法