精华内容
下载资源
问答
  • CPU缓存,提升缓存读取速率
    千次阅读
    2022-02-27 19:16:40

    CPU缓存,提升缓存读取速率

    原理:cpu读取数据是一段段(Cache Line 通常是64字节)读取,如果能将操作逻辑在一起的数据放在一块,能够减少cpu读取缓存带来的损耗

    示例:array[j][i]执行的时间是后者 array[i][j]的 8 倍之多

    因为二维数组 array 所占用的内存是连续的,比如若长度 N 的值为 2,那么内存中从前至后各元素的顺序是:
    array[0][0],array[0][1],array[1][0],array[1][1]。

    具体示例

    CPU Cache Line ,它定义了缓存一次载入数据的大小,Linux 上你可以通过 coherency_line_size 配置查看它,通常是 64 字节。

    Nginx,它是用哈希表来存放域名、HTTP 头部等数据的,这样访问速度非常快,而哈希表里桶的大小如server_names_hash_bucket_size,它默认就等于 CPU Cache Line 的值。

    Linux 操作系统,可以通过一个名叫 Perf 的工具直观地验证缓存命中的情况(可以用 yum install perf 或者 apt-get install perf 安装这个工具,这个网址中有大量案例可供参考)。

    更多相关内容
  • 使用Golang原语的CPU缓存行无效 介绍 一直是并行性的陷阱。 对于在高速缓存行边界内分配的共享上下文上同时进行读/写更新的应用程序,性能上的损失变得明显。 现代CPU倾向于利用高速缓存来频繁更新内存位置。 CPU...
  • CPU缓存

    万次阅读 多人点赞 2018-06-17 12:15:55
    CPU缓存是什么 CPU缓存(Cache Memory)是位于CPU与内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。CPU高速缓存的出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾,因为CPU...

    这里写图片描述

    CPU缓存是什么

    CPU缓存(Cache Memory)是位于CPU与内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。CPU高速缓存的出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾,因为CPU运算速度要比内存读写速度快很多,这样会使CPU花费很长时间等待数据到来或把数据写入内存。在缓存中的数据是内存中的一小部分,但这一小部分是短时间内CPU即将访问的,当CPU调用大量数据时,就可先缓存中调用,从而加快读取速度。

    当CPU需要读取数据并进行计算时,首先需要将CPU缓存中查到所需的数据,并在最短的时间下交付给CPU。如果没有查到所需的数据,CPU就会提出“要求”经过缓存从内存中读取,再原路返回至CPU进行计算。而同时,把这个数据所在的数据也调入缓存,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。

    缓存大小是CPU的重要指标之一,而且缓存的结构和大小对CPU速度的影响非常大,CPU内缓存的运行频率极高,一般是和处理器同频运作,工作效率远远大于系统内存和硬盘。实际工作时,CPU往往需要重复读取同样的数据块,而缓存容量的增大,可以大幅度提升CPU内部读取数据的命中率,而不用再到内存或者硬盘上寻找,以此提高系统性能。但是从CPU芯片面积和成本的因素来考虑,缓存都很小。

    CPU一级缓存、二级缓存、三级缓存是什么意思?

    一级缓存(L1 Cache)

    CPU一级缓存,就是指CPU的第一层级的高速缓存,主要当担的工作是缓存指令和缓存数据。一级缓存的容量与结构对CPU性能影响十分大,但是由于它的结构比较复杂,又考虑到成本等因素,一般来说,CPU的一级缓存较小,通常CPU的一级缓存也就能做到256KB左右的水平。

    二级缓存(L2 Cache)

    CPU二级缓存,就是指CPU的第二层级的高速缓存,而二级缓存的容量会直接影响到CPU的性能,二级缓存的容量越大越好。例如intel的第八代i7-8700处理器,共有六个核心数量,而每个核心都拥有256KB的二级缓存,属于各核心独享,这样二级缓存总数就达到了1.5MB。

    三级缓存(L3 Cache)

    CPU三级缓存,就是指CPU的第三层级的高速缓存,其作用是进一步降低内存的延迟,同时提升海量数据量计算时的性能。和一级缓存、二级缓存不同的是,三级缓存是核心共享的,能够将容量做的很大。

    这里写图片描述

    按照数据读取顺序和与CPU结合的紧密程度,CPU缓存可以分为一级缓存,二级缓存,如今主流CPU还有三级缓存,甚至有些CPU还有四级缓存。每一级缓存中所储存的全部数据都是下一级缓存的一部分,这三种缓存的技术难度和制造成本是相对递减的,所以其容量也是相对递增的。

    为什么CPU会有L1、L2、L3这样的缓存设计?主要是因为现在的处理器太快了,而从内存中读取数据实在太慢(一个是因为内存本身速度不够,另一个是因为它离CPU太远了,总的来说需要让CPU等待几十甚至几百个时钟周期),这个时候为了保证CPU的速度,就需要延迟更小速度更快的内存提供帮助,而这就是缓存。

    当CPU要读取一个数据时,首先从一级缓存中查找,如果没有找到再从二级缓存中查找,如果还是没有就从三级缓存或内存中查找。一般来说,每级缓存的命中率大概都在80%左右,也就是说全部数据量的80%都可以在一级缓存中找到,只剩下20%的总数据量才需要从二级缓存、三级缓存或内存中读取,由此可见一级缓存是整个CPU缓存架构中最为重要的部分。


    以下内容从参考链接复制

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

    简介:CPU缓存是CPU一个重要的组成部分,大家都知道三级缓存的重要性,但是知不知道三级缓存意味着什么,是不是三级缓存越大越好呢?让我们一起来看一下吧。

    缓存这个词想必大家都听过,其实缓存的意义很广泛:电脑整机最大的缓存可以体现为内存条、显卡上的显存就是显卡芯片所需要用到的缓存、硬盘上也有相对应的缓存、CPU有着最快的缓存(L1、L2、L3缓存等),缓存就是数据交换的缓冲区(称作Cache)。缓存往往都是RAM(断电即掉的非永久储存),它们的作用就是帮助硬件更快地响应。我们今天就来讲一下,关于最快的缓存——CPU缓存的那些事。

    CPU缓存是什么?

    CPU缓存的定义为CPU与内存之间的临时数据交换器,它的出现是为了解决CPU运行处理速度与内存读写速度不匹配的矛盾——缓存的速度比内存的速度快多了。CPU缓存一般直接跟CPU芯片集成或位于主板总线互连的独立芯片上。(现阶段的CPU缓存一般直接集成在CPU上)CPU往往需要重复处理相同的数据、重复执行相同的指令,如果这部分数据、指令CPU能在CPU缓存中找到,CPU就不需要从内存或硬盘中再读取数据、指令,从而减少了整机的响应时间。

    CPU-缓存-主内存

    CPU缓存速度和内存速度差多少?

    我们来简单地打个比方:如果CPU在L1一级缓存中找到所需要的资料要用的时间为3个周期左右,那么在L2二级缓存找到资料的时间就要10个周期左右,L3三级缓存所需时间为50个周期左右;如果要到内存上去找呢,那就慢多了,可能需要几百个周期的时间。

    I3-8300处理器技术规格

    对CPU缓存有一定了解了吗,让我们再深入一点。以Intel为例,Intel官网上产品-处理器界面内对缓存的定义为:“CPU高速缓存是处理器上的一个快速记忆区域。英特尔智能高速缓存(SmartCache)是指可让所有内核动态共享最后一级高速缓存的架构。”这里就提及到了最后一级高速缓存的概念,即为CPU缓存中的L3(三级缓存),那么我们继续来解释一下什么叫三级缓存,分别又是指哪三级缓存

    CPU-缓存-主内存图示

    三级缓存(L1、L2、L3)是什么?

    以近代CPU的视角来说,三级缓存(包括L1一级缓存、L2二级缓存、L3三级缓存)都是集成在CPU内的缓存,它们的作用都是作为CPU与主内存之间的高速数据缓冲区,L1最靠近CPU核心;L2其次;L3再次。运行速度方面:L1最快、L2次快、L3最慢;容量大小方面:L1最小、L2较大、L3最大。CPU会先在最快的L1中寻找需要的数据,找不到再去找次快的L2,还找不到再去找L3,L3都没有那就只能去内存找了。L1、L2、L3可以说是各有特点,下面我们就分开来讲一下。

    一级缓存(L1 Cache)

    一级缓存这个名词出现应该是在Intel公司Pentium处理器时代把缓存开始分类的时候,当时在CPU内部集成的CPU缓存已经不能满足整机的性能需求,而制造工艺上的限制不能在CPU内部大幅提高缓存的数量,所以出现了集成在主板上的缓存,当时人们把CPU内部集成的CPU缓存成为一级缓存,在CPU外部主板上的缓存称为二级缓存。

    而一级缓存其实还分为一级数据缓存(Data Cache,D-Cache,L1d)和一级指令缓存(Instruction Cache,I-Cache,L1i),分别用于存放数据及执行数据的指令解码,两者可同时被CPU访问,减少了CPU多核心、多线程争用缓存造成的冲突,提高了处理器的效能。一般CPU的L1i和L1d具备相同的容量,例如I7-8700K的L1即为32KB+32KB。

    二级缓存(L2 Cache)

    随着CPU制造工艺的发展,本来处于CPU外部的二级缓存也可以轻易地集成进CPU内部,这种时候再用缓存是否处于CPU内部来判断一二级缓存已经不再确切。集成进CPU的L2二级缓存运行速率渐渐可以跟上CPU的运行速度了,,其主要作用为当CPU在L1中没读取到所需要的数据时再把数据展示给CPU筛选(CPU未命中L1的情况下继续在L2寻求命中,缓存命中的工作原理我们稍后再讲)。

    L2二级缓存比L1一级缓存的容量要更大,但是L2的速率要更慢,为什么呢?首先L2比L1要更远离CPU核心,L1是最靠近CPU核心的缓存,CPU需要读取L2的数据从物理距离上比L1要更远;L2的容量比L1更大,打个简单的比喻,在小盒子里面找东西要比在大房间里面找要方便快捷。这里也可以看出,缓存并非越大越好,越靠近CPU核心的缓存运行速率越快越好,非最后一级缓存的缓存容量自然是够用即可。

    L2二级缓存实际上就是L1一级缓存跟主内存之间的缓冲器,在2006年的时间点上,Intel和AMD当家在售的几款处理器可以看出他们对最后一级缓存不同的见解:Intel Core Duo不同于它的前辈Pentium D、EE,采用了双核心共享的2M L2二级缓存,是属于当时最先二级缓存架构,即“Smart Cache”共享缓存技术,这种技术沿用到以后的Intel推出的所有多核心处理器上;而AMD Athlon 64 X2处理器则是每个CPU核心都具备独立的二级缓存,Manchester核心的处理器为每核心512KB、Toledo核心为每核心1MB,两个核心之间的缓存的数据同步是通过CPU内置的SRI(系统请求接口)控制,这样的数据延迟及占用资源情况都要比Intel的Pentium D、EE核心要好,但还是比不上Core为代表的Smart Cache缓存共享。

    三级缓存(L3 Cache)

    最初出现L3三级缓存的应该是AMD的K6-III处理器,当时受限于制造工艺,L3只能集成在主板上。然后Intel首次出现L3三级缓存的是Itanium安腾服务器处理器,接着就是P4EE和至强MP。L3三级缓存的出现其实对CPU性能提升呈一个爬坡曲线——L3从0到2M的情况CPU性能提升非常明显,L3从2M到6M提升可能就只有10%不到了,这是在近代CPU多核共享L3的情况下;当L3集成进CPU正式成为CPU内部缓存后,CPU处理数据时只有5%需要在内存中调用数据,进一步地减少了内存延迟,使系统的响应更为快速。

    同理,L3即为L2与主内存之间的缓冲器,主要体现在提升处理器大数据处理方面的性能,对游戏表现方面有较大的帮助。那么也许有人就会问了,是不是选择CPU的时候看准L3买,哪个CPU的L3大就买哪个?非也,只有同架构的情况下这种比较才具有意义,先举个比较久远的例子:Intel具备1MB L3的Xeon MP处理器仍然不是AMD没有L3的皓龙处理器对手,再来个现有的:Intel I7-8700K 12MB L3和AMD Threadripper 1950X 32MB L3相比,自然是32MB比12MB大,但是平均下来也是一个核心2MB L3,性能就见仁见智了。

    CPU缓存是怎样帮助CPU工作的呢

    知道了L1、L2、L3的由来,我们再深入地了解一下CPU缓存是怎么帮助CPU提高工作效率的。

    局限性原理

    由于数据的局限性,CPU往往需要在短时间内重复多次读取数据,内存的运行频率自然是远远跟不上CPU的处理速度的,怎么办呢?缓存的重要性就凸显出来了,CPU可以避开内存在缓存里读取到想要的数据,称之为命中(hit)。L1的运行速度很快,但是它的数据容量很小,CPU能在L1里命中的概率大概在80%左右——日常使用的情况下;L2、L3的机制也类似如此,这样一来,CPU需要在内存中读取的数据大概为5%-10%,其余数据命中全部可以在L1、L2、L3中做到,大大减少了系统的响应时间,总的来说,所有CPU读取数据的顺序都是先缓存再内存。

    L1、L2、L3缓存跟内存速度相差很大,它们构成上的不同导致了其速度的差距,那么CPU缓存和内存分别是怎样构成的呢?

    缓存SRAM与内存DRAM的区别

    CPU缓存基本由SRAM(Static RAM,静态RAM)构成,内存的DRAM其实是SDRAM(同步动态随机储存器),是DRAM(Dynamic RAM,动态)的一种。

    DRAM

    DRAM只含一个晶体管和一个电容器,集成度非常高,可以轻松做出大容量(内存),但是因为靠电容器来储存信息,所以需要不断刷新补充电容器的电荷,充电放电之间的时间差导致了DRAM比SRAM的反应要缓慢得多。

    SRAM

    SRAM相比DRAM的复杂度就高了不止一筹,所以导致SRAM的集成度很低——前期CPU缓存不能集成进CPU内部也有这个原因。SRAM的特点就是快,有电就有数据,不需要刷新时间所以凸显其数据传输速度很快,缺点就是占据面积大、成本低。假如一个DRAM占据一个单位的地方,一个SRAM就要占据六个单位的地方,差别还是挺大的。

    番外:L4四级缓存和eDRAM

    I7-4750H

    并不是每个CPU都会使用SRAM作为CPU缓存,IBM的Power系列处理器就使用了eDRAM作为CPU缓存;我们再看看Intel Haswell I7-4750H这个CPU,其主要受关注的地方在于CPU内嵌入了128MB的eDRAM作为显存让核心显卡Iris Pro 5200使用,在不使用核心显卡的时候,128MB eDRAM将会成为处理器的L4四级缓存。当然了,I7-4750H多了L4之后在处理器性能上也没提高多少,eDram缓存的主要作用还是在于给核心显卡当显存上。

    展开全文
  • 十分钟教你掌握CPU缓存

    千次阅读 多人点赞 2021-04-07 10:30:33
    基础知识 首先,大家都知道现在CPU的多核技术,都会有几级缓存,现在的CPU会有三级内存(L1,L2, L3),如下图所示。

    一、 基础知识

       首先,大家都知道现在CPU的多核技术,都会有几级缓存,现在的CPU会有三级内存(L1,L2, L3),如下图所示。

    在这里插入图片描述

    其中:

        L1缓存分成两种,一种是指令缓存,一种是数据缓存。L2缓存和L3缓存不分指令和数据。

    • L1和L2缓存在每一个CPU核中,L3则是所有CPU核心共享的内存。

    • L1、L2、L3的越离CPU近就越小,速度也就越快,越离CPU远,速度也越慢。

    再往后面就是内存,内存的后面就是硬盘。我们来看一些他们的速度。

    • L1的存取速度:4个CPU时钟周期
    • L2的存取速度:11个CPU时钟周期
    • L3的存取速度:39个CPU时钟周期
    • RAM内存的存取速度:107个CPU时钟周期

       我们可以看到,L1的速度是RAM的27倍,L1和L2的存取大小基本上是KB级的,L3则是MB级别的。例如,Intel Core i7-8700K,是一个6核的CPU,每核上的L1是64KB(数据和指令各32KB),L2是256K,L3有2MB。
    我们的数据从内存向上,先到L3,再到L2,再到L1,最后到寄存器进行计算。那么,为什么会设计成三层?这里有以下几方面的考虑:

    • 物理速度,如果要更大的容量就需要更多的晶体管,除了芯片的体积会变大,更重要的是大量的晶体管会导致速度下降,因为访问速度和要访问的晶体管所在的位置成反比。也就是当信号路径变长时,通信速度会变慢,这就是物理问题。

    • 另外一个问题是,多核技术中,数据的状态需要在多个CPU进行同步。我们可以看到,cache和RAM的速度差距太大。所以,多级不同尺寸的缓存有利于提高整体的性能。
         这个世界永远是平衡的,一面变得有多光鲜,另一方面也会变得有多黑暗,建立多级的缓存,一定就会引入其它的问题。这里有两个比较重要的问题。

    • 一个是比较简单的缓存命中率的问题

    • 另一个是比较复杂的缓存更新的一致性问题

       尤其是第二个问题,在多核技术下,这就很像分布式系统了,要面对多个地方进行更新。

    二、 缓存命中

       首先,我们需要了解一个术语Cache Line。缓存基本上来说就是把后面的数据加载到离自己最近的地方,对于CPU来说,它是不会一个字节一个字节的加载的。因为这非常没有效率,一般来说都是要一块一块的加载的,对于这样一块一块的数据单位,术语叫“Cache Line”。一般来说,一个主流的CPU的Cache Line是64 Bytes(也有的CPU用32Bytes和128Bytes),64Bytes也就是16个32位的数字,这就是CPU从内存中捞数据上来的最小数据单位。比如:Cache Line是最小单位(64Bytes),所以先把Cache分布多个Cache Line。比如:L1有32KB,那么 32KB/64Bytes = 512个Cache Line。
       缓存需要把内存里的数据放进来,英文叫CPU Associativity,Cache的数据放置策略决定了内存中的数据会拷贝到CPU Cache中的哪个位置上,因为Cache的大小远远小于内存,所以,需要有一种地址关联算法,能够让内存中的数据被映射到Cache中。这个就有点像内存地址从逻辑地址到物理地址的映射方法。但是不完全一样。

    基本上会有以下的一些方法

    • 任何一个内存的数据可以被缓存在任何一个Cache Line里,这种方法是最灵活的,但是,如果我们要知道一个内存是否存在于Cache中。我们就需要进行O(n)复杂度的Cache遍历,这是没有效率的。
    • 另一种方法,为了降低缓存搜索算法的时间复杂度,我们要使用像hash table这样的数据结构,最简单的hash table就是“求模运算”。比如,我们的L1 Cache有512个Cache Line,那么公式就是(内存地址 mod 512) *64就可以直接找到所在的Cache地址的偏移了。但是,这样的方式需要程序对内存地址的访问非常的平均,不然会造成严重地冲突。所以,这成了一个非常理想的情况了。
    • 为了避免上述的两种方案的问题,于是就要容忍一定的hash冲突,也就出现了N-Way关联。也就是把连续的N个Cache Line绑成一组,然后,先找到相关的组,然后再在组内找到相关的Cache Line。这叫Set Associativity。如下图所示
      在这里插入图片描述
         对于 N-Way 组关联,可能有点不好理解。这里举个例子,并多说一些细节(不然后面的代码你会不能理解),Intel 大多数处理器的L1 Cache都是32KB,8-Way 组相联,Cache Line 是64 Bytes。这意味着
    • 32KB的可以分成,32KB / 64 = 512 条 Cache Line;
    • 因为有8 Way,于是会每一Way 有 512 / 8 = 64 条 Cache Line;
    • 于是每一路就有 64 x 64 = 4096 Byts 的内存。

    为了方便索引内存地址

    • Tag:每条 Cache Line 前都会有一个独立分配的 24 bits来存的 tag,其就是内存地址的前24bits;
    • Index:内存地址后续的6个bits则是在这一Way的是Cache Line 索引,2^6 = 64 刚好可以索引64条Cache Line;
    • Offset:再往后的6bits用于表示在Cache Line 里的偏移量

    索引过程如下图所示:

    • 当拿到一个内存地址的时候,先拿出中间的 6bits 来,找到是哪组;
      在这里插入图片描述

    • 然后在这一个8组的cache line中,再进行O(n) ,n=8 的遍历,主是要匹配前24bits的tag。如果匹配中了,就算命中,如果没有匹配到,那就是cache miss,如果是读操作,就需要进向后面的缓存进行访问了。L2和L3同样是这样的算法。而淘汰算法有两种,一种是随机,另一种是LRU。
      在这里插入图片描述

    这也意味着:

    • L1 Cache 可映射 36bits 的内存地址,一共 2^36 = 64GB的内存
    • 当CPU要访问一个内存的时候,通过这个内存中间的6bits 定位是哪个set,通过前 24bits 定位相应的Cache Line。
    • 就像一个hash Table的数据结构一样,先是O(1)的索引,然后进入冲突搜索。 因为中间的 6bits决定了一个同一个set,所以,对于一段连续的内存来说,每隔4096的内存会被放在同一个组内,导致缓存冲突。

       此外,当有数据没有命中缓存的时候,CPU就会以最小为Cache Line的单元向内存更新数据。当然,CPU并不一定只是更新64Bytes,因为访问主存实在是太慢了,所以,一般都会多更新一些。好的CPU会有一些预测的技术,如果找到一种pattern的话,就会预先加载更多的内存,包括指令也可以预加载。这叫 Prefetching 技术。比如,你在for-loop访问一个连续的数组,你的步长是一个固定的数,内存就可以做到prefetching。

    了解这些细节,会有利于我们知道在什么情况下有可以导致缓存的失效。

    三、缓存一致

       对于主流的CPU来说,缓存的写操作基本上是两种策略

    • Write Back:写操作只在Cache上,然后再flush到内存上
    • Write Through:写操作同时写到cache和内存上。

       为了提高写的性能,一般来说,主流的CPU(如:Intel Core i7/i9)采用的是Write Back的策略,因为直接写内存实在是太慢了。

       好了,现在问题来了,如果有一个数据 x 在 CPU 第0核的缓存上被更新了,那么其它CPU核上对于这个数据 x 的值也要被更新,这就是缓存一致性的问题。

       一般来说,在CPU硬件上,会有两种方法来解决这个问题。

    1. Directory 协议。这种方法的典型实现是要设计一个集中式控制器,它是主存储器控制器的一部分。其中有一个目录存储在主存储器中,其中包含有关各种本地缓存内容的全局状态信息。当单个CPU Cache 发出读写请求时,这个集中式控制器会检查并发出必要的命令,以在主存和CPU Cache之间或在CPU Cache自身之间进行数据同步和传输。
    2. Snoopy 协议。这种协议更像是一种数据通知的总线型的技术。CPU Cache通过这个协议可以识别其它Cache上的数据状态。如果有数据共享的话,可以通过广播机制将共享数据的状态通知给其它CPU Cache。这个协议要求每个CPU Cache 都可以“窥探”数据事件的通知并做出相应的反应。如下图所示,有一个Snoopy Bus的总线。

    在这里插入图片描述

       因为Directory协议是一个中心式的,会有性能瓶颈,而且会增加整体设计的复杂度。而Snoopy协议更像是微服务+消息通讯,所以,现在基本都是使用Snoopy的总线的设计。

      在分布式系统中我们一般用Paxos/Raft这样的分布式一致性的算法。而在CPU的微观世界里,则不必使用这样的算法。因为CPU的多个核的硬件不必考虑网络会断会延迟的问题。所以,CPU的多核心缓存间的同步的核心就是要管理好数据的状态就好了。
       这里介绍几个状态协议,先从最简单的开始,MESI协议,这个协议跟那个著名的足球运动员梅西没什么关系,其主要表示缓存数据有四个状态:Modified(已修改), Exclusive(独占的),Shared(共享的),Invalid(无效的)。
        MESI 这种协议在数据更新后,会标记其它共享的CPU缓存的数据拷贝为Invalid状态,然后当其它CPU再次read的时候,就会出现 cache miss 的问题,此时再从内存中更新数据。从内存中更新数据意味着20倍速度的降低。我们能不能直接从我隔壁的CPU缓存中更新?是的,这就可以增加很多速度了,但是状态控制也就变麻烦了。还需要多来一个状态:Owner(宿主),用于标记,我是更新数据的源。于是,出现了 MOESI 协议。
        MOESI协议允许 CPU Cache 间同步数据,于是也降低了对内存的操作,性能是非常大的提升,但是控制逻辑也非常复杂。
        顺便说一下,与 MOESI 协议类似的一个协议是 MESIF,其中的 F 是 Forward,同样是把更新过的数据转发给别的 CPU Cache 但是,MOESI 中的 Owner 状态 和MESIF 中的 Forward 状态有一个非常大的不一样—— Owner状态下的数据是dirty的,还没有写回内存,Forward状态下的数据是clean的,可以丢弃而不用另行通知。
        需要说明的是,AMD用MOESI,Intel用MESIF。所以,F 状态主要是针对 CPU L3 Cache 设计的(前面我们说过,L3是所有CPU核心共享的)。

    四、程序性能

        了解了我们上面的这些东西后,我们来看一下对于程序的影响。

    示例一

       首先,假设我们有一个64M长的数组,设想一下下面的两个循环:

           const int LEN = 64*1024*1024;
           int *arr = new int[LEN];
    
              for (int i = 0; i < LEN; i += 2) arr[i] *= i;
    
              for (int i = 0; i < LEN; i += 8) arr[i] *= i; 
    
    
    

        按我们的想法,第二个循环要比第一个循环少4倍的计算量。其应该要快4倍的。但实际跑下来并不是,在我的机器上,第一个循环需要128毫秒,第二个循环则需要122毫秒,相差无几。这里最主要的原因就是 Cache Line,因为CPU会以一个Cache Line 64Bytes最小时单位加载,也就是16个32bits的整型,所以,无论你步长是2还是8,都差不多。而后面的乘法其实是不耗CPU时间的。

    示例二

       接下来,我们再来看个示例。下面是一个二维数组的两种遍历方式,一个逐行遍历,一个是逐列遍历,这两种方式在理论上来说,寻址和计算量都是一样的,执行时间应该也是一样的。

    const int row = 1024;
    const int col = 512
    int matrix[row][col];
    //逐行遍历
    int sum_row=0;
    for(int _r=0; _r<row; _r++) {
        for(int _c=0; _c<col; _c++){
            sum_row += matrix[_r][_c];
        }
    }
    //逐列遍历
    int sum_col=0;
    for(int _c=0; _c<col; _c++) {
        for(int _r=0; _r<row; _r++){
            sum_col += matrix[_r][_c];
        }
    }
    

       然而,并不是,在我的机器上,得到下面的结果。

       逐行遍历:0.083ms
       逐列遍历:1.072ms

       执行时间有十几倍的差距。其中的原因,就是逐列遍历对于CPU Cache 的运作方式并不友好,所以,付出巨大的代价。

    示例三

       接下来,我们来看一下多核下的性能问题,参看如下的代码。两个线程在操作一个数组的两个不同的元素(无需加锁),线程循环1000万次,做加法操作。在下面的代码中,我高亮了一行,就是p2指针,要么是p[1],或是 p[30],理论上来说,无论访问哪两个数组元素,都应该是一样的执行时间。

     void fn (int* data) {
        for(int i = 0; i < 10*1024*1024; ++i)
            *data += rand();
    }
    int p[32];
    int *p1 = &p[0];
    int *p2 = &p[1]; // int *p2 = &p[30];
    thread t1(fn, p1);
    thread t2(fn, p2);
    

       然而,并不是,在我的机器上执行下来的结果是:

       对于 p[0] 和 p[1] :570ms
       对于 p[0] 和 p[30]:105ms

       这是因为 p[0] 和 p[1] 在同一条 Cache Line 上,而 p[0] 和 p[30] 则不可能在同一条Cache Line 上 ,CPU的缓存最小的更新单位是Cache Line,所以,这导致虽然两个线程在写不同的数据,但是因为这两个数据在同一条Cache Line上,就会导致缓存需要不断进在两个CPU的L1/L2中进行同步,从而导致了5倍的时间差异。

    展开全文
  • 本文主要谈谈CPU缓存对Java编程的影响,不涉及具体CPU缓存的机制和实现。  现代CPU的缓存结构一般分三层,L1,L2和L3。如下图所示:  级别越小的缓存,越接近CPU, 意味着速度越快且容量越少。  L1是接近...
  • 从底层数据结构和CPU缓存两方面剖析LinkedList的查询效率为什么比ArrayList低。以前只直到数据结构会影响两者的查询效率,偶然间得知cpu缓存(硬件级别)也会有影响
  • CPU缓存与性能优化

    2020-05-02 23:48:01
    CPU缓存与性能优化 如何通过提升CPU缓存的命中率来优化程序的性能? 极客时间 - 陶辉 - CPU缓存:怎样写代码能够让CPU执行得更快? https://time.geekbang.org/column/article/230194 CPU缓存由更快的SRAM构成...

    CPU缓存与性能优化

    如何通过提升CPU缓存的命中率来优化程序的性能?
    极客时间 - 陶辉 - CPU缓存:怎样写代码能够让CPU执行得更快?
    https://time.geekbang.org/column/article/230194

    CPU缓存由更快的SRAM构成(内存由DRAM构成),而且离CPU核心更近,如果运算时需要的数据是从CPU缓存读取,而不是从内存中读取,运算速度就会快很多。

    RAM(Random Access Memory,随机存储器)主要的作用是储存代码和数据供CPU在需要的时候调用,可分为SRAM(Static Random Access Memory,静态随机存取存储器)和DRAM(Dynamic Random Access Memory,动态随机存取存储器)。

    “静态”是指不需要动态刷新电路以保持数据。SRAM使用4-6个晶体管存储一个bit,而DRAM只需要使用1个晶体管和1个电容就可以存储一个bit。DRAM进行读操作时会将电容与外界形成回路,通过检查电荷的流向来判断是1还是0(电荷从电容中流出为1,电荷流入到电容中为0),但电容中的电荷会随时间的变化逐渐流失,因此需要周期性地刷新电路。

    SRAM不需要刷新电路就可以保存内部的数据,所以具有更好的性能。

    但是正因为SRAM比DRAM使用更多的晶体管,所以相同容量的SRAM体积要比DRAM更大,即集成度更低,功耗也更大。

    综上所述,SRAM不需要周期性刷新电路,读写速度更快,但集成度低、功耗高、生产成本高,多用于容量较小的高速缓冲存储器;DRAM需要周期性刷新电路,读写速度慢,但集成度高、功能低、生产成本低,多用于容量较大的主存储器。

    优化越接近底层,适用范围越广,效果也越明显。理解CPU缓存的运行机制并编写顺应其工作方式代码,能够有效提升代码运行速度。

    CPU的多级缓存

    由于电子信号传输是需要时间的,离CPU核心越近,缓存的读写速度就越快。但CPU空间极小,所以离CPU越近缓存大小受到的限制也越大。所以,综合硬件布局、性能等因素,CPU缓存通常分为大小不等的三级缓存。

    查看CPU缓存大小:

    在这里插入图片描述
    在这里插入图片描述

    三级缓存要比一、二级缓存大很多倍。每个CPU核心都有自己的一、二级缓存,但三级缓存却是一颗CPU上所有核心共享的。

    程序执行时,会先将内存中的数据加载到共享的三级缓存中,再进入每个核心独享的二级缓存,最后进入最快的一级缓存(一级缓存有两个:一级数据缓存和一级指令缓存,如上图的index0和index1),之后才会被CPU使用。

    在这里插入图片描述

    缓存通常要比内存快很多。CPU访问一次内存通常需要100个时钟周期以上,而访问一级缓存只需要4~5个时钟周期,二级缓存需要12个时钟周期,三级缓存大约需要30个时钟周期。

    对于2GHz主频的CPU来说,一个时钟周期是:1s / (2 * 109Hz) = 0.5ns)。

    如果CPU要操作的数据在缓存中,则直接读取,即缓存命中。命中缓存会带来很大的性能提升,因此,我们的代码优化目标是提升CPU缓存的命中率

    CPU会区别对待指令和数据。比如计算1+1+是指令,会放在一级指令缓存中,1作为数据则放在一级数据缓存中。虽然在冯诺依曼计算机体系结构中,代码指令和数据是放在一起的,但执行时却要分开进入指令缓存和数据缓存。

    提升数据缓存的命中率

    数据的访问顺序会影响缓存命中率,那么是如何影响的呢?

    编写如下图所示的代码,测试数据的访问顺序是否会影响缓存命中率。

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    const N = 4096
    
    func main() {
    
    	var arr2D [N][N]int64
    
    	start := time.Now().UnixNano()
    	for i := 0; i < N; i++ {
    		for j := 0; j < N; j++ {
    			arr2D[i][j] = 0
    		}
    	}
    	ct1 := time.Now().UnixNano() - start
    
    	start = time.Now().UnixNano()
    	for i := 0; i < N; i++ {
    		for j := 0; j < N; j++ {
    			arr2D[j][i] = 0
    		}
    	}
    	ct2 := time.Now().UnixNano() - start
    
    	fmt.Printf("arr[j][i] costs %dns, arr[i][j] costs %dns, %vtimes", ct2, ct1, ct2 / ct1)
    }
    

    测试结果显示,arr[j][i]的执行时间大约是arr[i][j]的4倍以上。

    为什么会有这么大的差距呢?因为二维数组所占用的内存是连续的,比如N=2的数组中各元素的存储顺序为0_0, 0_1, 1_0, 1_1。如果用arr[i][j]遍历数组元素,则完全与数组在内存中存储的顺序一致,因此当CPU访问arr[0][0]时,也会将紧跟其后的3个元素加载到缓存中。如果用arr[j][i]遍历数组元素,访问顺序为0_0, 1_0, 0_1, 1_1,此时内存是跳跃访问的,如果N很大,那么操作arr[j][i]时,是无法将arr[j+1][i]也读入缓存的。

    缓存一次性会载入多少数据?是由什么因素确定的呢?

    缓存一次性载入的数据量与CPU Cache Line相关,定义了缓存一次载入数据的大小,Linux上可以通过coherency_line_size配置查看,通常是64 Bytes

    在这里插入图片描述

    当载入arr[0][0]时,若它占用的内存不足64字节时,CPU就会顺序地补足后续元素(当读取arr[0][1]时,CPU是怎么识别出来该元素在缓存中的?)。

    当遇到这种遍历访问数组的情况时,按照内存布局顺序访问将会带来很大的性能提升。

    查看指令缓存的命中率

    Linux下可以安装perf工具查看程序执行过程中CPU等各类资源的使用情况。

    提升指令缓存的命中率

    截图是留个坑,Ubuntu 18.04/Go暂时不能实现测试效果

    在这里插入图片描述

    CPU含有分支预测器。当代码中出现ifswitch等分支语句时,意味着CPU可以选择跳转到两段不同的指令去运行。如果分支预测器可以预测接下来要在哪段代码执行(比如if还是else中的指令),就可以提前把这些指令放在缓存中,CPU 执行时就会很快。当数组中的元素完全随机时,分支预测器无法有效工作,而当 array 数组有序时,分支预测器会动态地根据历史命中数据对未来进行预测,命中率就会非常高。

    在这里插入图片描述

    提升多核CPU下的缓存命中率

    如果进程A在时间片1里使用CPU核心1,则会使用到核心1的一、二级缓存,当时间片1结束后,操作系统会让进程A让出CPU,基于效率并兼顾公平的策略重新调度核心1,以防止某些进程长时间得不到CPU执行机会而导致假死现象。如果此时核心1繁忙,而核心2空闲,则进程A很可能被调度到核心2上执行。这样,即使对代码优化得再好,也只能在一个时间片内高效地使用CPU核心的一、二级缓存了,下一个时间片便面临着缓存效率的问题。

    因此,操作系统提供了将进程或者线程绑定到某一个CPU核心上运行的能力,如Linux上提供了syscall.SYS_SCHED_SETAFFINITY实现这一功能。如果当多线程同时执行密集计算,并且CPU命中缓存率很高时,如果将每个线程分别绑定在不同的CPU核心上,性能便会获得非常可观的提升。perf工具提供了cpu-migrations事件,以显示线程从不同的CPU核心上迁移的次数。

    Golang并不推荐这么做。

    总结

    1. 提高CPU缓存命中率对计算密集型程序有很大的性能提升;
    2. CPU缓存分为数据缓存和指令缓存:
      1. 对于数据缓存,应尽可能地顺序访问物理相邻的数据。
      2. 对于指令缓存,有规律的条件分支能够让CPU的分支预测发挥作用,进一步提升执行效率;对于多核系统,如果进/线程的缓存命中率非常高,则可以考虑绑定CPU核心来提升缓存命中率。

    思考

    多线程并行访问不同的变量,这些变量在内存布局是相邻的(比如类中的多个变量),此时CPU缓存就会失效,为什么?又该如何解决呢?

    当从内存加载数据到高速缓存时,每次cache line为基本单元读取数据,相邻的多个变量很可能在同一个数据单元中。当这个数据块被分别加载到两个CPU核心的高速缓存中时,只要其中一个核心对该数据块进行写操作,由于“缓存一致性”原则,每个处理器(核心)会通过嗅探在总线上传播的数据来检查自己的缓存值是否过期,一旦探测到缓存过期则重新从内存中加载到缓存中。

    可以通过将两个变量放到不同的缓存块中,来解决这个问题。

    参考

    1. 内存存储原理
    2. SRAM和DRAM的区别
    展开全文
  • CPU缓存 我们知道CPU的缓存一般是由三级缓存构成,缓存离CPU越近,CPU访问缓存的速度就越快。如下图所示,每个核心都有自己的一、二级缓存,但三级缓存却是一颗 CPU 上所有核心共享的;程序执行时,会先将内存中的...
  • CPU缓存失效导致性能下降:主板BIOS损坏导致CPU缓存失效.pdf
  •   CPU 缓存「Cache」指的访问速度比一般内存快得多的高速存储器,主要是为了解决 CPU 运算速率与内存读写速率不匹配的问题。因为 CPU 的运算速率比内存读写速率快得多,当 CPU 需要向内存请求数据或者写入数据时,...
  • 今天在看《架构解密》的时候,看到一段介绍CPU缓存一致性的介绍,文章详细解析了Intel多核处理器是如何解决数据一致性问题的,这本书在2020年看过一遍,现在又拿出来学习,感觉要学习的知识还有很多,关于CPU的多核...
  • 缓存锁就是指内存区域如果被缓存在处理器的缓存行中,并且在LOCK#操作期间,那么当它执行操作回写到内存时,处理器不在总线上声言LOCK#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性,...
  • CPU Cache Line伪共享问题的总结和分析》 以下文章来源于小林coding,作者小林coding Table of Contents CPU Cache 有多快? CPU Cache 的数据结构和读取过程是什么样的? 如何写出让 CPU 跑得更快的代码?...
  • 计算机硬件基础-----CPU缓存

    千次阅读 2022-02-10 19:13:50
    一、什么是缓存 一台电脑有两种内存 一种是动态随机存储器,它是在RAM(ram是芯片...CPU缓存CPU缓存是位于CPU与内存之间的临时数据交换器,它的容量比内存小的多但是交换速度却比内存要快得多。 2.2 CPU缓存出现..
  • 一个用于快速获取CPU缓存大小和行大小的库。 当前,此板条箱仅通过板条依靠CPUID指令,因此仅支持x86 CPU。 支持其他体系结构是一个目标。 欢迎公关! 请注意,该库仍将在非x86体系结构上编译和运行,但是所有...
  • 所谓的CPU缓存是CPU内部缓存的运行频率. 缓存的大小和结构对CPU速度的影响更大,因此缓存的大小也是CPU的重要性能指标之一. CPU缓存的工作效率可以远远超过内存和硬盘的速度. 以下安装主页主要介绍有关CPU缓存的知识...
  • 除了内存和硬盘,电脑还有一种超快速的存储设备,就是CPU缓存当你想到你电脑当中的存储设备时,你可能想到的是DDR内存、显卡上的显存、或者更有可能只是机械硬盘和SSD。但其实还有一种超快速的存储设备,对我们...
  • 在CPU的参数中,我们除了常见的核心线程以及各种频率之外,还经常会看到一个名为...因为CPU运算速度太快了,光靠内存读写完全跟不上,而CPU缓存的数据交换比内存快多了,大部分时候CPU可以直接从缓存读取数据。找不...
  • 如果你会去看CPU参数,应该有注意到CPU缓存这个东西,在CPU产品介绍上,通常也都可以看到,那么你知道CPU缓存是干嘛用的吗?内存条用来解决CPU和硬盘之间速度差距过大的问题。但是,即使有内存条,CPU的处理速度依然...
  • cpu缓存原理

    2016-11-21 16:04:12
    概述今天来跟大家分享一下cpu缓存相关的东西,在了解cpu缓存的工作原理时,举一反三,以后在学习一些缓存技术的实现的时候就会更加容易一些,现在那么多缓存技术,原理大多都大同小异。基本描述 我们都知道,CPU运算...
  • 为什么CPU缓存会分为L1、L2、L3?

    千次阅读 2019-10-01 19:12:40
    CPU缓存是CPU一个重要的组成部分,大家都知道三级缓存的重要性,但是知不知道三级缓存意味着什么,是不是三级缓存越大越好呢?让我们一起来看一下吧。 缓存这个词想必大家都听过,其实缓存的意义很广泛:电脑整机...
  • 一、什么是CPU缓存 1. CPU缓存的来历 众所周知,CPU是计算机的大脑,它负责执行程序的指令,而内存负责存数据, 包括程序自身的数据。在很多年前,CPU的频率与内存总线的频率在同一层面上。内存的访问速度仅比寄存器...
  • 浅谈CPU缓存的分级.pdf

    2021-09-24 22:48:47
    浅谈CPU缓存的分级.pdf
  • CPU缓存和伪共享

    千次阅读 2019-04-15 14:36:35
    CPU缓存 CPU是计算机的大脑,它负责执行程序的指令;内存负责存数据,包括程序自身数据。内存比CPU慢很多,现在获取内存中的一条数据大概需要200多个CPU周期(CPU cycles),而CPU寄存器一般情况下1个CPU周期就够了。 ...
  • CPU缓存L1/L2/L3工作原理

    千次阅读 2020-11-25 08:31:00
    那么,CPU缓存到底有多重要,它又是如何工作的? 二、什么是CPU缓存? 首先,缓存只是一种非常快速的内存类型。您可能知道,计算机内部有多种内存类型。有一个主存储(如硬盘或SSD),用于存储大量数据(操作系统和所有...
  • 在CPU和内存交换数据的时候,CPU内部的缓存才是关键的关键,今天小编就和大家分享一下关于CPU缓存的那些事儿。CPU缓存干啥用?现在电脑上的存储器,即便是Intel的傲腾内存,也比不上CPU内的缓存速度快。而且,缓存的...
  • 1. 安装CPU-Z这个软件 注:安装过程中的附加任务是指,在桌面添加快捷方式 ...x4是因为,英特尔所有的处理器每一个核心都是同时具备一级数据缓存的,有几个物理核心,CPU-Z就会在这里写上核心数目,图中的CPU为4
  • CPU缓存一致性协议MESI

    千次阅读 2018-09-05 21:46:09
    CPU高速缓存(Cache Memory) CPU为何要有高速缓存 CPU在摩尔定律的指导下以每18个月翻一番的速度在发展,然而内存和硬盘的发展速度远远不及CPU。这就造成了高性能能的内存和硬盘价格及其昂贵。然而CPU的高度运算...
  • 而cpu每次从内存中读取数据并不是只读取那个特定要访问的地址,而是读取一个数据块(视情况而定一段连续的内存地址)并保存到cpu的缓存中,然后下次访问内存数据的时候就会先从cpu缓存开始查找,如果找到就不需要再...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 461,323
精华内容 184,529
关键字:

cpu缓存

友情链接: 自适应波束形成.zip