cpu缓存与多线程
一、cpu缓存结构
CPU速度远高于内存(即如果只考虑CPU和内存因素,程序的性能常常受到内存访问速度的限制,内存访问和运行),为了协调CPU和内存在速度上的差异,在CPU中增加了高速缓存。和计算机存储金字塔结构类似,高速缓存也可以按照金字塔结构,从下到上越接近CPU速度越快,同时容量也越小。现在大部分的处理器都有二级或者三级缓存,从下到上依次为 L3 cache, L2 cache, L1 cache. 缓存又可以分为指令缓存和数据缓存,指令缓存用来缓存程序的代码,数据缓存用来缓存程序的数据。
单核CPU只含有一套L1,L2,L3缓存;如果CPU含有多个核心,即多核CPU,则每个核心都含有一套L1(甚至和L2)缓存,而共享L3(或者和L2)缓存。多CPU,每个CPU都相互独立,拥有自己的缓存,CPU之间无法共享缓存。 下图为一个单CPU双核的缓存结构。
缓存行 cache line
缓存行时缓存和内存交换数据的最小单位,一般为64字节。即每次缓存和内存之间进行数据交换的时候,都是以字节对齐的连续64字节的内存块整个进行。
缓存访问
没有缓存的结构中,CPU执行进程的时候,使用虚拟地址,虚拟地址通过MMU翻译为物理地址,映射到内存中;在有缓存的结构中,CPU访问数据先查找缓存中是否存在该数据,这可以通过数据在进程中的虚拟地址去到缓存中查找,也可以通过该数据在内存中的物理地址去到缓存中查找。
因此,缓存中的数据既可以通过数据在进程空间中的虚拟地址检索,也可以通过数据的实际内存物理地址来检索。如果通过进程空间中的虚拟地址,那么多个不同进程可能含有相同的虚拟地址,因此实际中还需要在缓存行中添加 ASID(address space identifier) the hardware version of a process ID,这样CPU在运行不同进程的时候,就可以通过 虚拟地址+ASID 在cache中进行查找;
如果缓存通过物理地址来检索,那么需要MMU介于CPU和cache中间来进行地址的翻译,这样就降低了cache查找的速率,因此一般 L1 cache不能通过物理地址检索,而是通过虚拟地址,由CPU直接访问。而L2,L3 cache可以通过物理内存地址检索。
TLB 快表
MMU进行虚拟地址到物理地址的映射,通过页表将虚拟地址翻译为物理地址。TLB位于MMU内部,不需要经过页表就可以将虚拟地址转换为物理地址,速度更快。
每一个TLB寄存器的每个条目包含一个页面的信息:有效位,虚页面号,修改位,保护码,和页面所在的物理页面号,它们和页面表中的表项一一对应。
MMU在翻译的时候,先查看虚拟地址的虚拟页面号是否存在TLB(并行的查找)中,如果存在,且没有违背读写权限限制,则直接给出TLB中的物理页面号;若在TLB中不存在,则进行常规的页表的查找,然后从TLB中淘汰一个条目,并更新为刚刚查找的页面。
多线程场景下的缓存
在单线程模式下,一块内存只对应一个cpu核心的缓存,且只被一个线程访问。缓存独占,不会出现访问冲突等问题。
在多线程模式下,一个CPU,且CPU单核,进程中的多个线程会同时访问进程中的共享数据,CPU将某块内存加载到缓存后,不同线程在访问相同的物理地址的时候,都会映射到相同的缓存位置,这样即使发生线程的切换,缓存仍然不会失效。但由于任何时刻只能有一个线程在执行,因此不会出现缓存访问冲突。当然,由于CPU内部寄存器的存在,会有多线程下的 A++ 非原子性语句导致的问题。
(
A++ ==>
reg = (&A);
reg ++;
(&A) = reg;
在发生线程切换的时候,线程使用的内部寄存器会被作为现场进行保存。
)
多线程模式下,一个CPU,且CPU有多核,每个核都至少有一个L1 cache。多个线程访问进程中的某个共享内存,且这多个线程分别在不同的核心上执行,则每个核心都会在各自的caehe中保留一份共享内存的缓冲。由于多核可以做到真并行,可能会出现多个线程同时写各自的cache,
因此CPU有“缓存一致性”原则,即每个处理器(核)都会通过嗅探在总线上传播的数据来检查自己的缓存值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器(核)。因此,我们经常看到在多核多线程的场景下,在声明变量时候使用volatile,volatile变量要求在更新了缓存之后立即写入到系统内存,而非volatile变量,则是CPU修改缓存,缓存在适当的识货(不知道什么时候)将缓存数据写入内存。写入内存的操作会出发其他处理器(核)将自己已经缓存的那块正在被写入的内存失效,并在下次需要使用到该内存的时候重新从内存读取。
False Sharing(伪共享)
由于缓存按照最小单位缓存行进行和内存交互,缓存行一般为64字节。内存中的连续64字节会被加载到一个缓存行中。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。
如上图所示,X和Y被放在同一缓存行中,当core1 修改x的时候,为了线程间可见性,需要锁定缓存行;此时core2中的缓存行也跟着失效;同样,core2中修改y时候,也需要锁定缓存行,core1中的缓存行也跟着失效。影响效率。
cpu缓存与多线程的更多相关文章
- python GIL 全局锁,多核cpu下的多线程性能究竟如何?
python GIL 全局锁,多核cpu下的多线程性能究竟如何?GIL全称Global Interpreter Lock GIL是什么? 首先需要明确的一点是GIL并不是Python的特性,它是在实现 ...
- C和C++中的volatile、内存屏障和CPU缓存一致性协议MESI
目录 1. 前言2 2. 结论2 3. volatile应用场景3 4. 内存屏障(Memory Barrier)4 5. setjmp和longjmp4 1) 结果1(非优化编译:g++ -g -o ...
- 基于JVM原理、JMM模型和CPU缓存模型深入理解Java并发编程
许多以Java多线程开发为主题的技术书籍,都会把对Java虚拟机和Java内存模型的讲解,作为讲授Java并发编程开发的主要内容,有的还深入到计算机系统的内存.CPU.缓存等予以说明.实际上,在实际的 ...
- 为什么CPU缓存会分为一级缓存L1、L2、L3?有什么意义?
https://baijiahao.baidu.com/s?id=1598811284058671259&wfr=spider&for=pc 简介:CPU缓存是CPU一个重要的组成部分 ...
- 从Java视角理解CPU缓存和伪共享
转载自:http://ifeve.com/from-javaeye-cpu-cache/ http://ifeve.com/from-javaeye-false-shari ...
- CPU缓存和内存屏障
CPU性能优化手段 - 缓存 为了提高程序的运行性能, 现代CPU在很多方面对程序进行了优化例如: CPU高速缓存, 尽可能的避免处理器访问主内存的时间开销, 处理器大多会利用缓存以提高性能 多级缓存 ...
- JMM内存模型、CPU缓存一致性原则(MESI)、指令重排、as-if-serial、happen-before原则
JMM三大特性原子性 汇编指令 --原子比较和交换在底层的支持 cmp-chxg 总线加锁机制 Synchronized Lock锁机制 public class VolatileAtomicSamp ...
- 【Java并发编程】从CPU缓存模型到JMM来理解volatile关键字
目录 并发编程三大特性 原子性 可见性 有序性 CPU缓存模型是什么 高速缓存为何出现? 缓存一致性问题 如何解决缓存不一致 JMM内存模型是什么 JMM的规定 Java对三大特性的保证 原子性 可见 ...
- 与程序员相关的CPU缓存知识
本文转载自与程序员相关的CPU缓存知识 基础知识 首先,我们都知道现在的CPU多核技术,都会有几级缓存,老的CPU会有两级内存(L1和L2),新的CPU会有三级内存(L1,L2,L3 ),如下图所示: ...
随机推荐
- 《python核心编程》读书笔记--第15章 正则表达式
15.1引言与动机 处理文本和数据是一件大事.正则表达式(RE)为高级文本匹配模式,为搜索-替换等功能提供了基础.RE是由一些字符和特殊符号组成的字符串,它们描述了这些字符和字符串的某种重复方式,因此 ...
- C#中进行单元测试
首先创建一个项目,写一段待测的程序: namespace ForTest { public class Program { static void Main(string[] args) { } pu ...
- Application_Error异常处理
/// <summary> /// 捕捉异常 /// </summary> protected void Application_Error() { // 获得前一个异常的实例 ...
- 洛谷 P1896 [SCOI2005]互不侵犯King
题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入输出格式 输入格式: 只有一行,包 ...
- Android Handler Message总结
http://blog.csdn.net/caesardadi/article/details/8473777 当应用程序启动时,会开启一个主线程(也就是UI线程),由她来管理UI,监听用户点击,来响 ...
- 【leetcode❤python】 112. Path Sum
#-*- coding: UTF-8 -*-# Definition for a binary tree node.# class TreeNode(object):# def __init_ ...
- CronTrigger:Corn表达式
定时触发 CronTrigger CronTriggers往往比SimpleTrigger更有用,如果您需要基于日历的概念,而非SimpleTrigger完全指定的时间间隔,复发的发射工作的时间表.C ...
- thinkphp的自动完成功能说明
手册里有一句话很关键: 自动完成是ThinkPHP提供用来完成数据自动处理和过滤的方法,使用create方法创建数据对象的时候会自动完成数据处理. 这句话说明自动完成发生的时间是create()组建数 ...
- factory工厂模式之抽象工厂AbstractFactory
* 抽象工厂: 意图在于创建一系列互相关联或互相依赖的对象. * 每个工厂都会创建一个或多个一系列的产品 * 适用于:产品不会变动,开始时所有产品都创建好,然后根据分类获取想要的 某一类产品(很像sp ...
- TestNg测试框架使用笔记
Gradle支持TestNG test { useTestNG(){ //指定testng配置文件 suites(file('src/test/resources/testng.xml')) } } ...