前文中我们用了一篇文章对内存做了分类,ARMv8和ARMv9只支持两种类型的内存,Device和Normal。而且这两种类型的内存是mutually-exclusive(互斥的),任何一段内存只能被赋予一种类型属性。我们在介绍Normal类型的时候简要的介绍过,Normal类型的内存属于Weakly-Ordered(弱排序),目的是为了最大成都加快编程指令流的执行,提高整个硬件平台的性能。正所谓甘蔗没有两头甜,有好的一面,自然伴随着一些影响,本文先来讨论ARM的弱排序内存模型。在正式进入课题讨论之前,我们可以闲聊一会儿,反正本公众号也是非著名与正经技术科普帖子集散地,哈哈哈。我们国家立国之初执行的是全面的计划经济体制,那个时代我们取得了非凡的成就:全国性的水利工程、全国性的交通体系、全国性的重工业体系、重大国防工程(两弹一星)、全国性的医疗卫生体系、全国性的教育体系...凡此种种,不胜枚举。一些列的政策,让我们的国家从鸦片战争之后,混乱、积贫积弱、被压迫奴役的局面迅速恢复了国力,人口基数得到恢复,国际地位也得到了显著的提高。随着时代的发展,在取得众多成就的基础上,我们发现国家的经济发展效率还不是特别高,比如资源配置僵化、国企效率低下、不能利用国际资本和更加先进的科学技术,于是我们摸着石头过河,打开国门开始了社会主义特色的市场经济的探索。实践证明,我们走对了路,改开之后,中国繁荣的经济发展让一代人享受到了红利,造就了很多经济奇迹和取得了很多举世瞩目的成就,强大让老大赶到了不安。可是,我们仍然没有放开管控,在市场经济迅猛发展的过程中,还是通过一些强有力的干预,让国家经济成功的一次又一次化险为夷,避免了几代人的奋斗成果被洗劫一空。一个伟人把这个战略称之为“两手抓,两手都要硬。”市场是无形之手,计划是有形之手,两只手握着中国这艘大船劈波斩浪驶向远方,注定会让我们完成民族的伟大复兴。The ARMv8 architecture employs a weakly-ordered model of memory. In general terms, this means that the order of memory accesses is not required to be the same as the program order for load and store operations. The processor is able to re-order memory read operations with respect to each other. Writes may also be re-ordered (for example, write combining) .As a result, hardware optimizations, such as the use of cache and write buffer, function in a way that improves the performance of the processor, which means that the required bandwidth between the processor and external memory can be reduced and the long latencies associated with such external memory accesses are hidden.
Armv8-A implements a weakly-ordered memory architecture. This architecture permits memory accesses which impose no dependencies to be issued or observed, and to complete in a different order from the order that is specified by the program order.These weakly-ordered memory behaviors are only permitted if:• The defined memory is Normal, Device-nGRE, or DeviceGRE;• Device-nR accesses span a peripheral.This guide(Order & Barrier) is suitable for developers of low-level code, such as boot code or drivers. It is particularly relevant to anyone writing code for multi-threaded applications or shared memory systems.(1) 所谓的order(顺序)指的是program order(编程顺序)。(2) weakly-ordered(弱排序)指的是和Program Order不一致了。(3) ARM架构的弱排序机制就是为了提高处理器的执行效率,提高整个系统的性能。(4) 弱排序和内存的类型密切相关,Normal, Device-nGRE, or DeviceGRE这些类型的内存都支持weakly-ordered架构。(5) Device-nR类型的内存在特殊的上下文也支持weakly-ordered架构。(6) 弱排序机制服务于性能,表现在运行时,但是要结合上下文作具体分析。(7) 由于弱排序会对编程顺序造成影响且不透明,因此编程时就要注意weakly-ordered对代码意图的影响。搞出这一切的就是Processor,是它被允许在指令流执行的时候便宜行事,改变了指令流的顺序,这就是那只“无形的手”,于无声处改变着计算机的世界。本文,我们就来研究这只无形的手,看看它是如何工作的。既然是Processor实现的指令重排,那么就要对Processor有起码的了解,比如CPU的架构,以及PE-Core的微架构,正所谓知己知彼。让我们来做一个简要回顾。(2) 熟悉ARM架构的朋友可以直接跳跃到1.1.2章节阅读。既然使用弱排序技术的触发源头是Processor,那么不了解它肯定是不行的。这里我们跳过SOC架构和总线架构,直接看一下ARM目前比较流行的CPU异构架构,如图1-1所示。图1-1 DSU-120 DynamIQ Cluster
The DynamIQ ™ Shared Unit-120 (DSU-120) provides a shared L3 memory system, snoop control and filtering, and other control logic to support a cluster of A-class architecture cores. The cluster is called the DSU-120 DynamIQ ™ cluster. Also, all the external interfaces to the System on Chip (SoC) are provided through the DSU-120.DSU架构下PE-Core可以根据使用场景进行动态且灵活的配置,按照此架构设计可以实现在CPU Cluster内部实现L3级别的Cache共享,而且可以轻松接入SOC,并且能够享受到一致性总线架构带来的便利。目前,打开高通和MTK比较新的芯片手册,都能够看到DSU的字样,说明芯片公司已经大规模采用了DSU技术,并应用于量产实践了(注意上面的框图只是一个示意图,不代表DSU只能支持集成6个PE-Core,就DSU-120而言,可以在一个CPU-Cluster部署超过14个PE-Core)。我们对图1-1做一下简化,看一个典型的DSU架构配置,如图1-2所示。图1-2 DynamIQ cluster with three types of cores
图1-2是一个目前比较流行的CPU架构的布局:大核、金核、银核。这里面,我们暂时知道这几个PE-Core是并行工作,且通过DSU架构保持Cache一致性即可。现在,我们把目光聚焦在一个PE-Core上,让我们来看一个具象化的实例,如图1-3所示。图1-3 Cortex-A510 High Level Micro-Arch
Cortex-A510 (codename Klein) is an ultra-high efficiency microarchitecture designed by ARM Holdings as a successor to the Cortex-A55. The Cortex-A510, which implements the ARMv9.0 ISA, is typically found in smartphone and other embedded devices. Often A510 cores are combined with higher performance processors (e.g. based on Cortex-A710) in DynamIQ big.LITTLE configuration to achieve better energy/performance.Cortex-A510是Cortex-A55的继任者,一般在DSU.Big.Little架构中扮演Little的角色,主要是在一些场景下扮演平衡功耗的角色。我们再来看一个高性能的PE-Core,如图1-4所示:图1-4 Cortex-A77 High Level Micro-Arch
Cortex-A77一般和Cortex-A55搭配组成DSU.Big.little架构的CPU,它扮演DSU架构中的big角色作为高性能PE-Core的使用,主要是应对游戏、视频等高算力消耗的场景。这一部分,我们不展开讨论,大家感兴趣可以阅读前序文章,或者自行阅读ARM手册。到这里,我们已经介绍了弱排序的基本概念、并且了解了ARM的CPU架构,(客人都到齐了,直接上菜),我们来看一下产生弱排序的上下文,进一步帮助大家理解ARM的弱排序内存架构。1.1.3 Reorder Context(弱排序的上下文)本节主要结合手册中的接受,带大家一起看一下有哪些场景下会有指令重排序的情况发生。Multiple issue of instructionsA processor might issue and execute multiple instructions per cycle, so that instructions that are after each other in program order can be executed at the same time.指令的并发执行,这个是现代处理器普遍具备的能力了,具体就是指在一个时钟周期可以同时并发执行多条指令。关于ARM指令集的架构我们前序文章也有介绍,这里我们我们也不展开讨论。根据图1-4,一个PE-Core可以在一个时钟周期从L1-Instruction-Cache中加载16个Bytes的数据,这16个Bytes数据会涵盖多条指令,经过译码后通过Issue模块发射给Execution Engine各个计算单元进行计算(整型和浮点型)、加载和存储数据,如图1-5所示。图1-5 Cortex-A77 Multi-ISSUE UNIT
通过图1-5很明显可以看出,一个PE-Core可以同事发射和执行多条指令。我们可以脑补一下,本来写代码的人编码的时候是One by One的形式编写的代码,到执行阶段可不是这么回事儿,PE-Core可以一次性的把一条指令后面的多条指令一次性加载并且同时执行,这就相当于改变了原来的指令的编程顺序。Many processors support out-of-order execution of non-dependent instructions. Whenever an instruction is stalled while it waits for the result of a preceding instruction, the processor can execute subsequent instructions that do not have a dependency.
When the processor encounters a conditional instruction, such as a branch, it can speculatively begin to execute instructions before it knows for sure whether that particular instruction must be executed or not. The result is, therefore, available sooner if conditions resolve to show the speculation was correct.Out-of-order,如图1-6所示。这个就是不装了,直接摊牌了。就是明确告诉你,处理器有自己的反馈系统,当前面的指令执行出现状况((分支判断、读存指令遇到缓存未命中的情况、等等),后面直接进行预测行为,调整指令的执行顺序,让后面可以执行的指令先动起来。此时,处理器可以不必严格按照程序中的指令顺序来执行指令,而是根据指令之间的依赖关系和数据可用性来动态调整执行顺序。图1-6 Cortex-A77 ReOrder UNIT
If a load instruction that reads from a cacheable location is speculatively executed, this can result in a cache linefill and the potential eviction of an existing cache line.
手册上介绍的这个场景时,大家可以忽略不必太过分计较和探究,可以跳过直接阅读下一个小结,不过为了文章的完整性,这里会结合图1-7 做一个简要分析。图1-7 Speculative loads Context
我们对上面的例子做一个概要的分析,帮助大家理解一下运行时的内存重排:(1) 首先我们假设在一个进程的上下文:Process-1的两个线程同时运行在两个PE-Core上面。(如果是两个CPU没有工作在一个进程的上下文的话,也可能造成因为内存重排导致的相关问题,因为进城之间也可以共享内存,但是那样话这个模型就更加的复杂了,不利于我们展开讨论。)(2) 当Process1 的 Thread1 在执行时遇到了存储指令,要将VAR的当前值写回Cache,稍后在合适的时机同步到外存。因为PE-Core的构造中在Cache和指令的发射单元之间是有buffer的,如图1-8所示。那么,Store指令执行后,VAR=7的值会优先存在Store Buffer中,然后再择机同步到Cache中去。图1-8 LSU Store Buffer Block
(3) 在Store指令执行的过程中,PE-Core预测到Store指令不会立即有结果,而下一条指令要Load的变量是Var1,与var的存储操作没有依赖关系,可以先执行,基于weakly-order的模型设计,那么Load var2的指令会提前执行。那么此时处理器的临界状态可能是这样的:• Store指令将Var = 7存储到Store Buffer中,但是还没有到Cache中。• Load指令用Var1的VA去Cache中寻找它所在的Cache Line 发生Miss。(4) 发生Miss后,PE-Core就要从外存中寻找Var1,并把它加载进Cache,此时就要执行Cache Line的替换策略,把当前Cache中的一个Cache Line 替换掉。假如就是要是替换掉Var所在的Cache Line,那么根据缓存一致性的协议,此时另外一个PE-Core的Cache中Var的值还是6。(当然这只是假设,PE的设计人员不会设计这样弱智的替换策略。首先我们是为了说明问题,其次如果此时在Store和Load指令中间还有几条指令是操作Var3 、Var4、 Var5...,这种情况还是有可能发生的。)(5) 这个时候,我们再来看一下SOC上存储系统,各个节点的状态,PE-Core(CPU-1)中Var的值是6,外存中Var的值也是6,但是PE-Core(CPU-0)中,Var=7。此时,站在外存和PE-Core(CPU-1)视角看PE-Core(CPU-0)就是发生了内存重排,而且造成了数据不一致的后果。这个Case需要大家对Cache的机制非常熟悉才行,特别是Cache Line的替换策略,以及缓存一致性机制有比较深入的了解,这些我们前面也有专门的文章讨论。虽然我们也会进行了分析,但是不会展开,大家只需要记住结论就行,不过感兴趣的同学可以继续研究,也希望大神多多指正。(1) 此种情况也是因为PE-Core的预测性指令执行引起的,已经可以证明了ARM的弱排序内存模型的工作的基本场景。(2) 此种情况与Device-nR在特殊的情况下被进行重排序操作(这个后面也会做讨论)一样,比较少见,而且处理器的优化操作要是为了支持这些场景下不发生错误,需要设计的更加复杂,会徒增很多不必要的成本,工程实践中应该比较少。(3) 本质上还是因为weakly-Order的内存架构会在临界状态下的引起内存重排,从而导致不确定性。(比如例子中的PE-Core(CPU-1) 在并发状态下根据var=6做出的某些判断,可能就会产生问题。)Load and store optimizationsAs reads and writes to external memory can have a long latency, processors can reduce the number of transfers by, for example, merging together a number of stores into one larger transaction.
图1-9 访问相同位置的执行模型
通过图1-9,我们可以很轻松的理解本小节要讲述的内容,指令1的操作为64位(0x1000 到 0x1007),指令2的操作为8位(0x1003),而一次总线的操作可以访问64位的空间(0x1000 到 0x1007),那么指令1和指令2对同一个位置的操作就可以合并到一起,而不需要浪费两次总线操作。In many complex System on Chip (SoC) devices, there are a number of agents capable of initiating transfers and multiple routes to the slave devices that are read or written. Some of these devices, such as a DRAM controller, might be capable of accepting simultaneous requests from different masters. Transactions can be buffered, or re-ordered by the interconnect. This means that accesses from different masters might therefore take varying numbers of cycles to complete and might overtake each other.图1-10 访问相同位置的执行模型
这一部分内容不展开讨论了,因为偏离了我们讨论的ARM架构的体系范畴。这里提到的优化更多的是在总线架构层面和DDR-Controller这个体系下描述一个内存控制器如何进行内存的重排序操作,如图1-10所示。这部分大家感兴趣可以自行研究。Cache coherent multi-core processingIn a multi-core processor, hardware cache coherency can migrate cache lines between cores. Different cores might therefore see updates to cached memory locations in a different order to each other.图1-11 PE-Core LSU Load Buffer UNIT
前面我们举例了写操作的时候,由于Store Buffer的存在导致了内存的重排序,各个观察者的看到内存不一致的情况存在。这里是因为Load Buffer的存在,当图1-7中的PE-Core(CPU-0)读取Var =6时,也是先读取到Load Buffer中,注意这次Load操作在PE-Core(CPU-0)的Issue模块中,并没有执行完。而此时,如果PE-Core(CPU-1)中Var发生改变比如Var=7,根据MESI的Cache一致性规范,PE-Core(CPU-0)中的Cache中Var也会变成7,这就和PE-Core(CPU-0)的LoadBuffer中的Var=6不一致了。这部分内容大家可以结合图1-7、1-11,自行分析了,这种由于多核并发导致的内存重排序非常好理解,我们不展开讨论。An optimizing compiler can re-order instructions to hide latencies or make best use of hardware features. It can often move a memory access forwards, to make it earlier, and give it more time to complete before the value is required. 图1-12 Optimizing compilers Reorder Model前面讲述的场景都是内存在运行时由于处理器的优化设计从而产生的内存重排序,而图1-12则展示了在编译阶段由交叉编译链工具产生的指令重排序现象,这部分也可以通过编译参数进行配置,这里我们不展开讨论了,感兴趣的同学可以自行研究。首先内存类型为Device-nR的内存是不支持Cache和被预测访问,而且不支持弱排序访问。但是,在加了一个条件之后“span a peripheral”,手册中说,这种类型的内存也支持弱排序架构的优化操作。这其实是与我们的认知是违背的,但是为了文章的完整性,这里只给出结论,感兴趣的同学可以自行研究。(1) 这种微架构设计,至少目前市场的商用处理器很少有对这种情况做优化处理的,因为要做非常精细的控制,处理器会更加复杂。当处理器执行Device-nR类型的内存访问时,它会检查访问的地址是否落在某个外设寄存器的地址范围内。如果访问的地址与某个外设寄存器的地址范围匹配,则处理器认为这次访问是针对该外设的。此时不需要做任何Reorder优化动作。如果处理器检测到连续的Device-nR类型内存访问涉及多个不同的外设寄存器地址范围,则它会认为这些访问跨越了多个外设。这通常发生在处理器需要同时与多个外设进行交互时,例如,在数据传输或设备控制过程中。此时还不能做Reorder优化动作,还需要做进一步的条件检查。如果访问之间不存在数据依赖或控制依赖,处理器可能会调整访问顺序以优化处理器的执行效率。然而,由于Device-nR属性限制了访问的重排序,处理器在处理这些访问时需要特别小心,以避免破坏外设的访问顺序和数据一致性。In a single core system, the effects of such re-ordering are generally transparent to the programmer, as the individual processor can check for hazards and ensure that data dependencies are respected. However, in cases where you have multiple cores that communicate through shared memory, or share data in other ways, memory ordering considerations become more important. 其实归纳起来就是一句话,无论是在编译阶段还是运行时阶段,这种内存弱排序的优化设计,会导致一定的不确定性。这种不确定性会导致,代码的实际执行逻辑和程序员设计时候的逻辑可能不一致,从而导致执行结果错误。其实前面我们零零散散的也透露了一些影响,这里我们就举一个最简答的例子加以说明,如图1-13所示。图1-13 Reorder demo
(1) 如果没有Reorder优化,那么最后指令3执行之后,0x12会返回给寄存器W2。(2)如果执行了Reorder优化,由于指令1&2,可能会考虑执行总线优化,将相同区域的访问合并成一次总线事务。此时指令3可能会被提前执行,那么此时返回给寄存器W2的就是0x12了。显然,这不是编程者想看到的。本文我们从一只无形的手开始,详细的介绍了ARM的弱排序内存模型的基本概念。在回顾了CPU的架构以及微架构之后,对产生弱排序行为的上下文做了详细的分析,最后通过一个简单的例子表明了弱排序内存可能存在的影响。不论是编译器重排还是运行期的重排,优点就是保证了指令流的持续执行,提高了CPU的执行效率,缺点就是在一些情况下会给代码的执行带来结果不确定性。虽然多数场景下,处理在执行优化动作弱排序之前,都是会经过充分评估规避风险的,但是程序员如果不放心的情况还是存在的,ARM的架构设计也是讲道理的,它提供了途径让码农们安心,那就是内存屏障(Barrier),下一篇文章,我们会详细讨论,请大家保持关注。[00] <corelink_dmc520_technical_reference_manual_en.pdf>[01] <corelink_dmc620_dynamic_memory_controller_trm.pdf>[02] <IP-Controller/DDI0331G_dmc340_r4p0_trm.pdf>[03] <80-ARM-IP-cs0001_ARMv8基础篇-400系列控制器IP.pdf>[04] <arm_cortex_a725_core_trm_107652_0001_04_en.pdf>[05] <DDI0487K_a_a-profile_architecture_reference_manual.pdf>[06] <armv8_a_address_translation.pdf>[07] <cortex_a55_trm_100442_0200_02_en.pdf>[08] <learn_the_architecture_aarch64_memory_management_guide_en.pdf>[09] <learn_the_architecture_armv8-a_memory_systems_en.pdf>[10] <79-LX-LK-z0002_奔跑吧Linux内核-V-2-卷1_基础架构.pdf>[11] <79-LX-LD-s003-Linux设备驱动开发详解4_0内核-3rd.pdf>[12] <learn_the_architecture_memory_systems_ordering_and_barriers.pdf>[13] <arm_dsu_120_trm_102547_0201_07_en.pdf>[14] <80-ARM-MM-AL0001_内存学习(三):物理地址空间.pdf>[15] <80-ARM-MM-cs0002_运行期重排序:内存系统的重排序.pdf>
MMU - Memory Management UnitTLB - translation lookaside bufferVIPT - Virtual Index Physical TagVIVT - Virtual Index Virtual TagPIPT - Physical Index Physical TagIPS - Intermediate Physical SpaceIPA - Intermediate Physical AddressVMID - virtual machine identifierTLB - translation lookaside buffer(地址变换高速缓存)VTTBR_EL2 - Virtualization Translation Table Base RegistersASID - Address Space Identifier (ASID)DMC - Dynamic Memory ControllerDDR SDRAM - Double Data Rate Synchronous Dynamic Random Access MemoryDMB - Data Memory BarrierDSB - Data Synchronization Barrier ISB - Instruction Synchronization BarrierDSU - DynamIQ ™ Shared Unit