一、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缓存与多线程的更多相关文章

  1. python GIL 全局锁,多核cpu下的多线程性能究竟如何?

    python GIL 全局锁,多核cpu下的多线程性能究竟如何?GIL全称Global Interpreter Lock GIL是什么? 首先需要明确的一点是GIL并不是Python的特性,它是在实现 ...

  2. C和C++中的volatile、内存屏障和CPU缓存一致性协议MESI

    目录 1. 前言2 2. 结论2 3. volatile应用场景3 4. 内存屏障(Memory Barrier)4 5. setjmp和longjmp4 1) 结果1(非优化编译:g++ -g -o ...

  3. 基于JVM原理、JMM模型和CPU缓存模型深入理解Java并发编程

    许多以Java多线程开发为主题的技术书籍,都会把对Java虚拟机和Java内存模型的讲解,作为讲授Java并发编程开发的主要内容,有的还深入到计算机系统的内存.CPU.缓存等予以说明.实际上,在实际的 ...

  4. 为什么CPU缓存会分为一级缓存L1、L2、L3?有什么意义?

    https://baijiahao.baidu.com/s?id=1598811284058671259&wfr=spider&for=pc 简介:CPU缓存是CPU一个重要的组成部分 ...

  5. 从Java视角理解CPU缓存和伪共享

    转载自:http://ifeve.com/from-javaeye-cpu-cache/               http://ifeve.com/from-javaeye-false-shari ...

  6. CPU缓存和内存屏障

    CPU性能优化手段 - 缓存 为了提高程序的运行性能, 现代CPU在很多方面对程序进行了优化例如: CPU高速缓存, 尽可能的避免处理器访问主内存的时间开销, 处理器大多会利用缓存以提高性能 多级缓存 ...

  7. JMM内存模型、CPU缓存一致性原则(MESI)、指令重排、as-if-serial、happen-before原则

    JMM三大特性原子性 汇编指令 --原子比较和交换在底层的支持 cmp-chxg 总线加锁机制 Synchronized Lock锁机制 public class VolatileAtomicSamp ...

  8. 【Java并发编程】从CPU缓存模型到JMM来理解volatile关键字

    目录 并发编程三大特性 原子性 可见性 有序性 CPU缓存模型是什么 高速缓存为何出现? 缓存一致性问题 如何解决缓存不一致 JMM内存模型是什么 JMM的规定 Java对三大特性的保证 原子性 可见 ...

  9. 与程序员相关的CPU缓存知识

    本文转载自与程序员相关的CPU缓存知识 基础知识 首先,我们都知道现在的CPU多核技术,都会有几级缓存,老的CPU会有两级内存(L1和L2),新的CPU会有三级内存(L1,L2,L3 ),如下图所示: ...

随机推荐

  1. 解决浏览器使用<pre></pre>时不换行

    <!-- 解决火狐浏览器中pre标签不换行 --> <style type="text/css"> pre { white-space: pre-wrap; ...

  2. Linux下mysql主从配置

    mysql服务器的主从配置,这样可以实现读写分离,也可以在主库挂掉后从备用库中恢复需要两台机器,安装mysql,两台机器要在相通的局域网内主机A: 192.168.1.100从机B:192.168.1 ...

  3. spark1.4

    spark1.4 Windows local调试环境搭建总结 1.scala版本 scala-2.10.4 官方推荐 scala-2.11.7[不推荐,非sbt项目.需要后加载] 2.spark版本 ...

  4. MVCAction接受与返回

    //Action方法接受有:如下四种: //Test(int id) 接受url路由中配置的同名参数 //通过request.Form/request.querystring 接受(get) //Te ...

  5. 是否用new来新建对象

    class A{ }: 1.不使用new来新建对象 A a: 使用完后什么也不用做,系统自动调用析构函数.使用空间是栈. 2.使用new来新建对象 A* a=new A();   delete a;/ ...

  6. Centos6.6上安装mysql5.6中的一些典型问题

    经过两天的摸索,终于成功在CentOS6.6系统上成功安装了mysql5.6,现整理如下. (1)安装时的问题: 最小化安装后,安装rpm包时经常会遇到 linux/centos Header V3 ...

  7. .Net鼠标随动窗口

    就像QQ宠物或者迅雷悬浮窗口一样,鼠标点下去窗体跟着鼠标动 主要是两个时间的加载 MouseDown和MouseMove事件 MouseDown事件: private int _StartX ;//鼠 ...

  8. :判断101-200之间有多少个素数,并输出所有素数。 程序分析:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除, 则表明此数不是素数,反之是素数。

    package C; public class Sushu { public static void main(String[] args) { int sum=0; for (int i = 101 ...

  9. 单向和双向tvs管

    tvs管器件按极性可分为单极性和双极性两种,即单向tvs管和双向tvs管.    单向tvs管保护器件仅能对正脉冲或者负脉冲进行防护,而双向tvs管保护器件一端接要保护的线路,一端接地,无论来自反向还 ...

  10. div相对浏览器移动

    <%    String path = request.getContextPath();%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTM ...