FalseSharing
发布日期:2021-06-29 18:38:30
浏览次数:2
分类:技术文章
本文共 2407 字,大约阅读时间需要 8 分钟。
文章目录
FalseSharing
CPU缓存架构
- CPU 和主内存间有好几级缓存,
- 直接访问主内存很慢
- 若对一块数据做相同的运算多次,执行运算时把它加载到离 CPU 很近的地方
- 如循环计数,不想每次循环都跑到主内存去取这个数据来增长它吧。
- L1 很小很快,紧靠着在使用它的 CPU 内核。
- L2 大些,慢些,只能被一个单独的核使用。
- L3 在现代多核机器中更普遍,更大,更慢,且被单个插槽上的所有CPU核共享。
- 主存保存着程序运行的所有数据,更大,更慢,
- 由全部插槽上的所有 CPU 核共享。
- CPU运算时,先去L1找,再去L2,然后L3,
- 最后如果这些缓存中都没有,所需的数据就要去主内存拿。
- 走得越远,运算耗时就越长。
- so 若进行很频繁的运算,要确保数据在 L1 缓存。
CPU缓存行
-
缓存由缓存行组成,常64字节(旧的处理器缓存行32)
- 它有效地引用主内存中的一块地址。
-
一个缓存行中可存 8 个 java的long
-
程序运行时,缓存每次更新都从主内存中加载连续64
-
设想有个long型a,是单独的变量,另个long 型变量 b 挨着它,
- 当加载a时将免费加载b。
-
若核的线程在对 a 改,另一个核的线程却对 b 读。
-
当前者改a 时,会把a和b同时加载到前者核心的缓存行中,更新完a后其它所有包含a的缓存行都将失效
- 因为其它缓存中的a不是最新值了。
-
这就出现问题,
-
b 和 a 不相干,每次却要因为 a 的更新需要从主内存重新读取,它被缓存未命中给拖慢了。
-
此即传说中的伪共享。
伪共享
- 当多线程修改互相独立的变量时,若这些变量共享同一个缓存行,就会无意中影响彼此的性能,
- 即伪共享。
- 下面例子说明了伪共享是咋肥事。
public class FalseSharingTest { public static void main(String[] args) throws InterruptedException { testPointer(new Pointer()); } private static void testPointer(Pointer pointer) throws InterruptedException { long start = System.currentTimeMillis(); Thread t1 = new Thread(() -> { for (int i = 0; i < 100000000; i++) { pointer.x++; } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 100000000; i++) { pointer.y++; } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(System.currentTimeMillis() - start); System.out.println(pointer); }}class Pointer { volatile long x; volatile long y;}
- 声明Pointer类,含x和y (必须声明为volatile,保证可见性,关于内存屏障后面再讲),一个线程对 x 自增1亿,一个线程对 y 自增1亿。
- x 和 y 没关系,但更新 x 时会把其它包含 x 的缓存行失效,同时也就失效了 y,运行这段程序输出的时间为3890ms。
避免伪共享
- 一个缓存行64 字节,
- long 8 字节,
- 避免伪共享很简单
在两个 long 类型的变量之间再加 7 个 long 类型
- 把Pointer改成下面
class Pointer { volatile long x; long p1, p2, p3, p4, p5, p6, p7; volatile long y;}
- 运行程序,时间为695
重新创建自己的 long 类型,而不是 java 自带的 long
- 修改Pointer
class Pointer { MyLong x = new MyLong(); MyLong y = new MyLong();}class MyLong { volatile long value; long p1, p2, p3, p4, p5, p6, p7;}
- pointer.x++; 改pointer.x.value++;,pointer.y++; 改为pointer.y.value++;,
- 间724
使用 @sun.misc.Contended 注解(java8)
- 修改 MyLong 如下:
@sun.misc.Contendedclass MyLong { volatile long value;}
- 默认使用这个注解是无效的,需要在JVM启动参数加上-XX:-RestrictContended才生效,,
- 再次运行程序718ms。
- 以上三种方式中的前两种是通过加字段的形式实现的,加的字段没有地方使用,
- 可能会被jvm优化掉,建议用第三种。
参考链接
- https://www.jianshu.com/p/7758bb277985
转载地址:https://cyj666.blog.csdn.net/article/details/103244660 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
不错!
[***.144.177.141]2024年04月22日 21时16分59秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
使用FILEUPLOAD控件将EXCEL文导入并保存至数据库
2019-04-30
ASP.NET 2.0个性化配置(profile)
2019-04-30
ASP.NET之:序列化
2019-04-30
Asp.Net 构架(HttpModule 介绍) - Part.3
2019-04-30
深入浅出分析C#接口的作用
2019-04-30
免费的天气预报webservice接口
2019-04-30
Server.Transfer VS Response.Redirect
2019-04-30
asp.net页面出错时常用的处理方法
2019-04-30
ASP.NET State Service
2019-04-30
web.config中的InProc模式 与 StateServer模式
2019-04-30
C#泛型集合揽胜
2019-04-30
如何选择书籍
2019-04-30
linux下清空文件内容
2019-04-30
Linux查看某目录占用空间以及其下有多少个文件
2019-04-30
ORA-01950:+对表空间+'USERS'+无权限
2019-04-30
CSS样式语法基础
2019-04-30
oracle 执行存储过程返回值
2019-04-30
C#调用存储过程的通用类
2019-04-30
如何使<a>标签中的 href 属性动态绑定
2019-04-30