C++性能榨汁机之伪共享
C++性能榨汁机之伪共享
来源 http://irootlee.com/juicer_false_sharing/
前言
在多核并发编程中,如果将互斥锁的争用比作“性能杀手”的话,那么伪共享则相当于“性能刺客”。“杀手”与“刺客”的区别在于杀手是可见的,遇到杀手时我们可以选择战斗、逃跑、绕路、求饶等多种手段去应付,但“刺客”却不同,“刺客”永远隐藏在暗处,伺机给你致命一击,防不胜防。具体到我们的并发编程中,遇到锁争用影响并发性能情况时,我们可以采取多种措施(如缩短临界区,原子操作等等)去提高程序性能,但是伪共享却是我们从所写代码中看不出任何蛛丝马迹的,发现不了问题也就无法解决问题,从而导致伪共享在“暗处”严重拖累程序的并发性能,但我们却束手无策。
缓存行
为了进行下面的讨论,我们需要首先熟悉缓存行的概念,学过操作系统课程存储结构这部分内容的同学应该对存储器层次结构的金字塔模型印象深刻,金字塔从上往下代表存储介质的成本降低、容量变大,从下往上则代表存取速度的提高。位于金字塔模型最上层的是CPU中的寄存器,其次是CPU缓存(L1,L2,L3),再往下是内存,最底层是磁盘,操作系统采用这种存储层次模型主要是为了解决CPU的高速与内存磁盘低速之间的矛盾,CPU将最近使用的数据预先读取到Cache中,下次再访问同样数据的时候,可以直接从速度比较快的CPU缓存中读取,避免从内存或磁盘读取拖慢整体速度。
CPU缓存的最小单位就是缓存行,缓存行大小依据架构不同有不同大小,最常见的有64Byte和32Byte,CPU缓存从内存取数据时以缓存行为单位进行,每一次都取需要读取数据所在的整个缓存行,即使相邻的数据没有被用到也会被缓存到CPU缓存中(这里又涉及到局部性原理,后面文章会进行介绍)。
缓存一致性
在单核CPU情况下,上述方法可以正常工作,可以确保缓存到CPU缓存中的数据永远是“干净”的,因为不会有其他CPU去更改内存中的数据,但是在多核CPU下,情况就变得更加复杂一些。多CPU中,每个CPU都有自己的私有缓存(可能共享L3缓存),当一个CPU1对Cache中缓存数据进行操作时,如果CPU2在此之前更改了该数据,则CPU1中的数据就不再是“干净”的,即应该是失效数据,缓存一致性就是为了保证多CPU之间的缓存一致。
Linux系统中采用MESI协议处理缓存一致性,所谓MESI即是指CPU缓存的四种状态:
- M(修改,Modified):本地处理器已经修改缓存行,即是脏行,它的内容与内存中的内容不一样,并且此 cache 只有本地一个拷贝(专有);
- E(专有,Exclusive):缓存行内容和内存中的一样,而且其它处理器都没有这行数据;
- S(共享,Shared):缓存行内容和内存中的一样, 有可能其它处理器也存在此缓存行的拷贝;
- I(无效,Invalid):缓存行失效, 不能使用。

每个CPU缓存行都在四个状态之间互相转换,以此决定CPU缓存是否失效,比如CPU1对一个缓存行执行了写入操作,则此操作会导致其他CPU的该缓存行进入Invalid无效状态,CPU需要使用该缓存行的时候需要从内存中重新读取。由此就解决了多CPU之间的缓存一致性问题。
伪共享
何谓伪共享?上面我们提过CPU的缓存是以缓存行为单位进行的,即除了本身所需读写的数据之外还会缓存与该数据在同一缓存行的数据,假设缓存行大小是32字节,内存中有“abcdefgh”八个int型数据,当CPU读取“d”这个数据时,CPU会将“abcdefgh”八个int数据组成一个缓存行加入到CPU缓存中。假设计算机有两个CPU:CPU1和CPU2,CPU1只对“a”这个数据进行频繁读写,CPU2只对“b”这个数据进行频繁读写,按理说这两个CPU读写数据没有任何关联,也就不会产生任何竞争,不会有性能问题,但是由于CPU缓存是以缓存行为单位进行存取的,也是以缓存行为单位失效的,即使CPU1只更改了缓存行中“a”数据,也会导致CPU2中该缓存行完全失效,同理,CPU2对“b”的改动也会导致CPU1中该缓存行失效,由此引发了该缓存行在两个CPU之间“乒乓”,缓存行频繁失效,最终导致程序性能下降,这就是伪共享。
如何避免伪共享
避免伪共享主要有以下两种方式:
- 缓存行填充(Padding):为了避免伪共享就需要将可能造成伪共享的多个变量处于不同的缓存行中,可以采用在变量后面填充字节的方式达到该目的。
- 使用某些语言或编译器中强制变量对齐,将变量都对齐到缓存行大小,避免伪共享发生。
总结
一般伪共享都很隐蔽,很难被发现,当伪共享真正构成性能瓶颈的时候,我们有必要去努力找到并解决它,但是在大部分对性能追求没有那么高的应用中,伪共享的存在对程序的危害很小,有时并不值得耗费精力和额外的内存空间(缓存行填充)去查找系统存在的伪共享。还是那句我一直以来遵循的话“不要过度优化,不要提前优化。”。
==================== End
C++性能榨汁机之伪共享的更多相关文章
- 伪共享(false sharing),并发编程无声的性能杀手
在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor ...
- 并发性能的隐形杀手之伪共享(false sharing)
在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor ...
- 多线程中的volatile和伪共享
伪共享 false sharing,顾名思义,“伪共享”就是“其实不是共享”.那什么是“共享”?多CPU同时访问同一块内存区域就是“共享”,就会产生冲突,需要控制协议来协调访问.会引起“共享”的最 ...
- 伪共享和缓存行填充,从Java 6, Java 7 到Java 8
关于伪共享的文章已经很多了,对于多线程编程来说,特别是多线程处理列表和数组的时候,要非常注意伪共享的问题.否则不仅无法发挥多线程的优势,还可能比单线程性能还差.随着JAVA版本的更新,再各个版本上减少 ...
- java中伪共享问题
伪共享(False Sharing) 原文地址:http://ifeve.com/false-sharing/ 作者:Martin Thompson 译者:丁一 缓存系统中是以缓存行(cache l ...
- 关于java中的伪共享的认识和解决
在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素: CPU缓存 网页浏览器为了加快速度,会在本机存缓存以前浏览过 ...
- Java 中的伪共享详解及解决方案
1. 什么是伪共享 CPU 缓存系统中是以缓存行(cache line)为单位存储的.目前主流的 CPU Cache 的 Cache Line 大小都是 64 Bytes.在多线程情况下,如果需要修改 ...
- 伪共享(False Sharing)
原文地址:http://ifeve.com/false-sharing/ 作者:Martin Thompson 译者:丁一 缓存系统中是以缓存行(cache line)为单位存储的.缓存行是2的整数 ...
- 杂谈 什么是伪共享(false sharing)?
问题 (1)什么是 CPU 缓存行? (2)什么是内存屏障? (3)什么是伪共享? (4)如何避免伪共享? CPU缓存架构 CPU 是计算机的心脏,所有运算和程序最终都要由它来执行. 主内存(RAM) ...
随机推荐
- Hearthstone AI
search keyword `machine learning hearthstone` with google I am a legend: Hacking Hearthstone with ma ...
- 在python中使用正则表达式(转载)
https://www.cnblogs.com/hanmk/p/9143514.html 在python中使用正则表达式(一) 在python中通过内置的re库来使用正则表达式,它提供了所有正则表 ...
- [Java读书笔记] Effective Java(Third Edition) 第 6 章 枚举和注解
Java支持两种引用类型的特殊用途的系列:一种称为枚举类型(enum type)的类和一种称为注解类型(annotation type)的接口. 第34条:用enum代替int常量 枚举是其合法值由一 ...
- STM32命名规则解析
- Path环境变量的作用
作用: 当我们要求系统运行一个程序(例如a.exe)而没有告诉它程序所在的完整路径时,系统会先在当前目录寻找是否存在a.exe,如果找到,直接运行:如果没有找到,会去path路径下面找.设置path, ...
- 如何创建spring web 工程(maven工程)
1.在项目资源管理器右键,New-Spring Starter Project 2.设置一些参数 3.点击Next,然后勾选两个选项 4.点击finish
- codevs 1048/洛谷 1880:石子归并
题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1].问安排怎样的合并顺序,能够使 ...
- 《剑指offer》查找二维数组内元素 c++
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序. 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. cl ...
- 《MIT 6.828 Lab 1 Exercise 7》实验报告
本实验链接:mit 6.828 lab1 Exercise 7. 题目 Exercise 7. Use QEMU and GDB to trace into the JOS kernel and st ...
- notepad++一次多行复制粘贴到对应位置
NodePad++一次复制多行粘贴到对应位置 有时候要写sql,但是里面有很多字段要对应上,如果要自己一个字段一个字段的去写是在有点麻烦,是不是有更好的方法做到这件事呢? 要做这件事,首先分析下我们已 ...