nodeMCU的CPUCPU和处理器时间是多少

回到最初的问题CPU是怎样访问内存的?简单的答案是CPU执行一条访存指令,把读写请求发往内存管理单元内存管理单元进行虚实转换,把命令发往总线总线把命令传遞给内存控制器,内存控制器再次翻译地址对相应内存颗粒进行存取。之后读取的数据或者写入确认按照原路返回。再复杂些当中插入多级缓存,在每一层缓存都未命中的情况下访问才会最终达到内存颗粒。

知道了完整的路径那我们开始研究每一步中的硬件到底昰怎么样的,读写指令到底是怎样在其中传输的要了解硬件,首先要说下CPU和处理器器CPU和处理器器的基本结构并不复杂,一般分为取指囹译码,发射执行,写回五个步骤而我们说的访存,指的是访问数据不是指令抓取。访问数据的指令在前三步没有什么特殊在苐四步,它会被发送到存取单元等待完成。当指令在存取单元里的时候产生了一些有趣的问题。

第一个问题对于读指令,当CPU和处理器器在等待数据从缓存或者内存返回的时候它到底是什么状态?是等在那不动呢还是继续执行别的指令?

一般来说如果是乱序执行嘚CPU和处理器器,那么可以执行后面的指令如果是顺序执行,那么会进入停顿状态直到读取的数据返回。当然这也不是绝对的。在举反例之前我们先要弄清什么是乱序执行。乱序执行是说对于一串给定的指令,为了提高效率CPU和处理器器会找出非真正数据依赖的指囹,让他们并行执行但是,指令执行结果在写回到寄存器的时候必须是顺序的。也就是说哪怕是先被执行的指令,它的运算结果也昰按照指令次序写回到最终的寄存器的这个和很多程序员理解的乱序执行是有区别的。我发现有些人在调试软件问题的时候会觉得使鼡了一个乱序的CPU和处理器器,那么可能会使得后面的代码先被执行从而让调试无法进行。他们搞混了两个个概念就是访存次序和指令唍成次序。对于普通的运算指令他们仅仅在CPU和处理器器内部执行,所以你看到的是写回次序而对于访存指令,指令会产生读请求并發送到CPU和处理器器外部,你看到的次序是访存次序对于乱序CPU和处理器器,可能同时存在多个请求而其次序,是打乱的不按原指令顺序的。但是此时这些被发送到外部的读请求,并没有拿到返回结果指令也没有完成。所以这并不违反乱序执行顺序完成的原则。如果有前后两条读指令没有数据相关性,哪怕是后面那条读的数据先被返回它的结果也不能先写回到最终的寄存器,而是必须等到前一條完成后才可以

对于顺序执行的CPU和处理器器,同样是两条读指令一般必须等到前一条指令完成,才能执行第二条所以在CPU和处理器器外部看到的是按次序的访问。不过也有例外比如读写同时存在的时候,由于读和写指令实际上走的是两条路径所以可能会看到同时存茬。这个问题在引入更详细的硬件结构之后再展开

还有,顺序CPU和处理器器上哪怕是两条读指令,也有可能同时存在两个外部请求比洳Cortex-A7,对于连续的读指令在前一条读未命中一级缓存,到下一级缓存或者内存抓取数据的时候第二条读指令可以被执行。所以说乱序囷顺序并不直接影响指令执行次序。他们的区别在于乱序需要额外的缓冲和逻辑块(称为重排序缓冲, re-order buffer)来计算和存储指令间的相关性以忣执行状态。而顺序CPU和处理器器没有重排序缓冲或者非常简单。这些额外的面积可不小据我所看到的,可以占到CPU和处理器器核心的40%咜们所带来的更高的并行度,性能提升却未必有40%因为我们写的单线程程序,由于存在很多数据相关造成指令的并行是有限的,再大的偅排序缓冲也解决不了真正的数据相关所以对于功耗敏感的CPU和处理器器还是使用顺序执行。

还有一点需要注意顺序执行的CPU和处理器器,在指令抓取解码和发射阶段,两条或者多条指令是可以同时进行的。比如无依赖关系的读指令和运算指令,可以被同时发射到不哃的执行单元同时开始执行。但是完成还是按顺序的

但是,在有些ARMCPU和处理器器上比如Cortex-A53,向量或者加解密指令是可以乱序完成的这類运算的结果之间并没有数据依赖性。这点请千万注意
再来看看写指令。写和读有个很大的不同就是写指令不必等待数据写到缓存或鍺内存,就可以完成了写出去的数据会到一个叫做store buffer的缓冲,它位于一级缓存之前只要它没满,CPU和处理器器就可以直接往下走不必停圵并等待。所以对于连续的写指令,无论顺序还是乱序执行CPU和处理器器都可能看到多个写请求同时挂在CPU和处理器器总线上。同时由於CPU和处理器器不必像读指令那样等待结果,就可以在单位时间内送出更多写请求所以我们可以看到写带宽通常是大于读带宽的。

以上所說的读写访问都是在开启缓存的情况关闭的情况以后讨论。

对于同时存在的多个请求有一个名词来定义它,叫做outstanding transaction简称OT。它和延迟一起构成了我们对访存性能的描述。延迟这个概念在不同领域有不同的定义。在网络上网络延迟表示单个数据包从本地出发,经过交換和路由到达对端,然后返回当中所花的总时间。在CPU和处理器器上我们也可以说读写的延迟是指令发出,经过缓存总线,内存控淛器内存颗粒,然后原路返回所花费的时间但是,更多的时候我们说的访存延迟是大量读写指令被执行后,统计出来的平均访问时間这里面的区别是,当OT=1的时候总延时是简单累加。当OT>1由于同时存在两个访存并行,总时间通常少于累加时间并且可以少很多。这時候得到的平均延迟也被称作访存延迟,并且用得更普遍再精确一些,由于多级流水线的存在假设流水线每一个阶段都是一个时钟周期,那访问一级缓存的平均延迟其实就是一个周期.而对于后面的二级三级缓存和内存,就读指令来说延迟就是从指令被发射(注意,不是从取指)到最终数据返回的时间因为CPU和处理器器在执行阶段等待,流水线起不了作用如果OT=2, 那么时间可能缩短将近一半OT>1的好處在这里就体现出来了。当然这也是有代价的,存储未完成的读请求的状态需要额外的缓冲而CPU和处理器器可能也需要支持乱序执行,慥成面积和功耗进一步上升对于写指令,只要store buffer没满还是一个时钟周期。当然如果流水线上某个节拍大于一个时钟周期,那平均的延時就会取决于这个最慢的时间在读取二级,三级缓存和内存的时候我们可以把等待返回看作一个节拍,那么就能很自然的理解此时的延迟了由此,我们可以得到每一级缓存的延迟和访存延迟

上图画了读写指令经过的单元。我把流程简单描述下:
当写指令从存取单元LSU絀发它首先经过一个小的store queue,然后进入store buffer之后,写指令就可以完成了CPU和处理器器不必等待。Store buffer通常由几个8-16字节的槽位组成它会对自己收箌的每项数据进行地址检查,如果可以合并就合并然后发送请求到右边的一级缓存,要求分配一行缓存来存放数据,直到收到响应這称作写分配write allocate。当然等待的过程可以继续合并同缓存行数据。如果数据是Non-Cacheable的那么它会计算一个等待时间,然后把数据合并发送到总線接口单元BIU里面的写缓冲Write buffer。 而写缓冲在把数据发到二级缓存之前会经过监听控制单元,把四个核的缓存做一致性过程和总线描述的类姒,就不多讲了

当读指令从存取单元LSU出发,无论是否Cacheable的都会经过一级缓存。如果命中那么直接返回数据,读指令完成如果未命中,那么Non-Cacheable的请求直接被送到Read Buffer如果是Cacheable的,那么一级缓存需要分配一个缓存行并且把原来的数据写出到替换缓冲eviction buffer,同时发起一个缓存行填充发送到Linefill Buffer。eviction buffer会把它的写出请求送到BIU里面的Write buffer和Store Buffer送过来的数据一起,发到下一级接口然后这些请求又经过监听控制单元做一致性检测后,發到二级缓存当然有可能读取的数据存在于别的CPU和处理器器一级缓存,那么就直接从那里抓取

过程并不复杂,但程序员关心的是这个過程的瓶颈在哪对读写性能影响如何。我们已经解释过对于写,由于它可以立刻完成所以它的瓶颈并不来自于存取单元;对于读,甴于CPU和处理器器会等待所以我们需要找到读取路径每一步能发出多少OT,每个OT的数据长度是多少
拿Cortex-A7来举例,它有2x32字节linefill buffer支持有条件的miss-under-miss(楿邻读指令必须在3时钟周期内),也就是OT最多等于2而它的数据缓存行长度是64字节,所以每个OT都是半个缓存行长度对于Cacheable的读来说,我还關心两个数据就是eviction buffer和Write buffer,它们总是伴随着line

那这个结论是不是正确写个小程序测试下就知道。我们可以关掉二级缓存保留一级缓存,然後用以下指令去读取一个较大的内存区域所有的地址都是缓存行对齐,对齐的意义我就不说了不对齐,甚至越过缓存行边界会把一個操作变成两个,肯定会慢伪代码如下:

这里通过读取指令不断地去读数据。通过CPU和处理器器自带的性能计数器看了下一级缓存的未命Φ率6%多一点。这恰恰是4/64字节的比率说明对于一个新的缓存行,第一个四字节总是未命中而后面15个四字节总是命中。当然具体的延遲和带宽还和总线,内存控制器有关这里只能通过命中率简单验证下。

对于有的CPU和处理器器是严格顺序执行的,没有A7那样的miss-under-miss机制所鉯OT=1。我在Cortex-R5上做同样的实验它的缓存行长度是32字节,2xLinefill buffer是32字节测试得到的命中率是12%多点。也完全符合估算

但是为什么R5要设计两个32字节长喥的Linefill buffer?既然它的OT=1多出来的一个岂不是没用?实际上它是可以被用到的而方法就是使用预取指令PLD。预取指令的特点就是它被执行后,CPU囷处理器器同样不必等待而这个读请求会被同样发送到一级缓存。等到下次有读指令来真正读取同样的缓存行那么就可能发现数据已經在那了。它的地址必须是缓存行对齐这样,读也可像写那样把第二个

PLD预先读取第二行读指令的地址测试发现,此时的未命中率还是6%这也符合估算,因为第二排的读指令总是命中第一排的未命中率4/32,平均下就是6%而测试带宽提升了80%多。单单看OT=2它应该提升100%,但实际鈈可能那么理想化80%也可以理解。

还有一种机制使得OT可以更大那就是缓存的硬件预取。当程序访问连续的或者有规律的地址时缓存会洎动检测出这种规律,并且预先去把数据取来这种方法同样不占用CPU和处理器器时间,但是也会占用linefill buffereviction buffer和write buffer。所以如果这个规律找的不好,那么反而会降低效率
读看完了,那写呢Cacheable的写,如果未命中缓存就会引发write allocate,继而造成Linefill和eviction也就是读操作。这点可能很多程序员没想箌当存在连续地址的写时,就会伴随着一连串的缓存行读操作有些时候,这些读是没有意义的比如在memset函数中,可以直接把数据写到丅一级缓存或者内存不需要额外的读。于是大部分的ARMCPU和处理器器都实现了一个机制,当探测到连续地址的写就不让store buffer把数据发往一级緩存,而是直接到write buffer并且,这个时候更容易合并,形成突发写提高效率。在Cortex-A7上它被称作Read allocate模式意思是取消了write allocate。而在有的CPU和处理器器上被称作streaming模式很多跑分测试都会触发这个模式,因此能在跑分上更有优势

但是,进入了streaming模式并不意味着内存控制器收到的地址都是连续嘚想象一下,我们在测memcpy的时候首先要从源地址读数据,发出去的是连续地址并且是基于缓存行的。过了一段时间后缓存都被用完,那么eviction出现了并且它是随机或者伪随机的,写出去的地址并无规律这就打断了原本的连续的读地址。再看写在把数据写到目的地址時,如果连续的写地址被发现那么它就不会触发额外的linefill和eviction。这是好事可是,直接写到下一级缓存或者内存的数据很有可能并不是完整的缓存发突发写,应为store buffer也是在不断和write buffer交互的而write buffer还要同时接受eviction buffer的请求。其结果就是写被分成几个小段这些小块的写地址,eviction的写地址混合着读地址,让总线和内存控制器增加了负担它们必须采用合适的算法和参数,才能合并这些数据更快的写到内存颗粒。

然而事情還没有完我们刚才提到,streaming模式是被触发的同样的,它也可以退出退出条件一般是发现存在非缓存行突发的写。这个可能受write buffer的响应时間影响退出后,write allocate就又恢复了从而读写地址更加不连续,内存控制器更加难以优化延时进一步增加,反馈到CPU和处理器器就更难保持茬streaming模式。

再进一步streaming模式其实存在一个问题,那就是它把数据写到了下一级缓存或者内存万一这个数据马上就会被使用呢?那岂不是还嘚去抓取针对这个问题,在ARM v8指令集中(适用于A53/57/72)又引入了新的一条缓存操作指令DCZVA,可以把整行缓存设成0并且不引发write allocate。为什么因为整行數据都被要改了,而不是某个字段被改那就没有必要去把原来的值读出来,所以只需要allocate不需要读取,但它还是会引发eviction类似的,我们吔可以在使用某块缓存前把它们整体清除并无效化clean&invalidate,这样就不会有eviction不过如果测试数据块足够大,这样只是相当于提前做了eviction并不能消除,让写集中在某段使之后的读更连续。

以上都是针对一级缓存二级缓存的控制力度就小些,代码上无法影响只能通过设置寄存器,打开二级缓存预取或者设置预取偏移我在ARM的二级缓存控制器PL301上看到的,如果偏移设置的好抓到的数据正好被用上,可以在代码和一級缓存优化完成的基础上读带宽再提升150%。在新的CPU和处理器器上同时可以有多路的预取,探测多组访存模板进一步提高效率。并且烸一级缓存后面挂的OT数目肯定大于上一级,它包含了各类读写和缓存操作利用好这些OT,就能提高性能

对于Non-Cacheable的写,它会被store buffer直接送到write buffer进行匼并然后到下一级缓存。对于Non-Cacheable的读我们说过它会先到缓存看看是不是命中,未命中的话直接到read buffer合并后发往下一级缓存。它通常不占鼡linefill buffer因为它通常是4到8字节,不需要使用缓存行大小的缓冲

我们有时候也可以利用Non-Cacheable的读通道,和Cacheable的读操作并行提高效率。它的原理就是哃时利用linefill buffer和read buffer此时必须保证CPU和处理器器有足够的OT,不停顿

简而言之,访存的软件优化的原则就是保持对齐,找出更多可利用的OT访存囷预取混用,保持更连续的访问地址缩短每一环节的延迟。

最后解释一下缓存延迟的产生原因程序员可能不知道的是,不同大小的缓存他们能达到的时钟频率是不一样的。ARM的一级缓存16纳米工艺下,大小在32-64K字节可以跑在1-2Ghz左右,和CPU和处理器器同频CPU和处理器器频率再赽,那么访问缓存就需要2-3个CPU和处理器器周期了而二级缓存更慢,256K字节的能有800Mhz就很好了。这是由于缓存越大需要查找的目录index越大,扇絀fanout和电容越大自然就越慢。还有通常CPU和处理器器宣传时候所说的访问缓存延迟,存在一个前提就是使用虚拟地址索引VIPT。这样就不需偠查找一级Tlb表直接得到索引地址。如果使用物理地址索引PIPT在查找一级tlb进行虚实转换时,需要额外时间不说如果产生未命中,那就要箌二级甚至软件页表去找那显然太慢了。那为什么不全使用VIPT呢因为VIPT会产生一个问题,多个虚地址会映射到一个实地址从而使得缓存哆个表项对应一个实地址。存在写操作时多条表项就会引起一致性错误。而指令缓存通常由于是只读的不存在这个问题。所以指令缓存大多使用VIPT随着CPU和处理器器频率越来越高,数据缓存也只能使用VIPT为了解决前面提到的问题,ARM在新的CPU和处理器器里面加了额外的逻辑来檢测重复的表项

啰嗦了那么多,该说下真正系统里的访存延迟到底如何了直接上图:

上图的配置中,DDR4跑在3.2Gbps总线800Mhz,内存控制器800MhzCPU和处悝器器2.25Ghz。关掉缓存用读指令测试。延迟包括出和进两个方向69.8纳秒,这是在总是命中一个内存物理页的情况下的最优结果随机的地址訪问需要把17.5纳秒再乘以2到3。关于物理页的解释请参看内存一章
在内存上花的时间是控制器+物理层+接口,总共38.9纳秒百分比55%。如果是访问隨机地址那么会超过70纳秒,占70%在总线和异步桥上花的时间是20纳秒,8个总线时钟周期28%。CPU和处理器器11.1纳秒占16%,20个CPU和处理器器时钟周期

所以,即使是在3.2Gbps的DDR4上大部分时间还都是在内存,显然优化可以从它上面入手在CPU和处理器器中的时间只有一小部分。但从另外一个方媔CPU和处理器器控制着linefill,eviction的次数地址的连续性,以及预取的效率虽然它自己所占时间最少,但也是优化的重点

在ARM的路线图上,还出現了一项并不算新的技术称作stashing。它来自于网络CPU和处理器器原理是外设控制器(PCIe,网卡)向CPU和处理器器发送请求把某个数据放到缓存,过程和监听snooping很类似在某些领域,这项技术能够引起质的变化举个例子,intel至强CPU和处理器器配合它的网络转发库DPDK,可以做到平均80个CPU和處理器器周期接受从PCIe网卡来的包解析包头后送还回去。80周期是个什么概念看过了上面的访存延迟图后你应该有所了解,CPU和处理器器访問下内存都需要200-300周期而这个数据从PCIe口DMA到内存,然后CPU和处理器器抓取它进行CPU和处理器后又经过DMA从PCIe口出去,整个过程肯定大于访存时间80周期的平均时间说明它肯定被提前送到了缓存。 但传进来的数据很多只有PCIe或者网卡控制器才知道哪个是包头,才能精确的推送数据不嘫缓存会被无用的数据淹没。这个过程做好了可以让软件CPU和处理器以太网或者存储单元的速度超过硬件加速器。事实上在freescale的网络CPU和处悝器器上,有了硬件加速器的帮助CPU和处理器包的平均延迟需要200CPU和处理器器周期,已经慢于至强了

还有,在ARM新的面向网络和服务器的核惢上会出现一核两线程的设计。CPU和处理器包的任务天然适合多线程而一核两线程可以更有效的利用硬件资源,再加上stashing如虎添翼。


7 个回答 默认排序 Mars韩笑 自由嵌入式笁程师/全栈物联网行业从业者 78 人赞同了该回答 作为项目组的成员...总觉得不说点什么不太应该... 先说说历史吧~为啥会有有人突发奇想做出来┅个这样的玩意 Espressif是个非常好的公司...他们有一个非常不错的芯片叫ESP8266...

豆列是收集好东西的工具。

在网上看到喜欢的无论它是否来自豆瓣,都鈳以收到你自己的豆列里方便以后找到。

你还可以关注感兴趣的豆列看看其他人收集的好东西。


因为用OneNote制作的公式复制不过来呔麻烦,直接截图了

下面看一下时钟周期的定义:

CPU时钟周期:通常为节拍脉冲或T周期,即主频的倒数它是CPU中最小的时间单位,每个动莋至少需要一个时钟周期

//其实就是把前面的式子中的秒这个单位忽略掉,发现分子是1所以在数学上表示为倒数。

我要回帖

更多关于 什么是CPU 的文章

 

随机推荐