如果你的代码直接和硬件交互,或者和其他核执行的代码交互,或者直接加载要执行的指令,或修改页表,你就需要注意内存排序的问题。
ARMv8采用了内存的弱序(weakly-ordered)模式。这意味着实际访问内存的顺序和你程序里写的不一定是一致的。处理器可以对读写内存的指令重新排序,以达到优化性能的目的。读写普通内存(Normal memory)时,硬件可以进行重新排序,只受数据依赖性和内存屏障指令的影响。
下面是几个重新排序的例子:

  • 多指令发射

处理器可能在一个时钟周期内发射并执行多条指令,这样在程序中有先后顺序的指令实际上就可以同时执行。

  • 乱序执行

很多处理器支持对无依赖关系的指令进行乱序执行。当一条指令在等待之前指令的结果时,处理器可以先执行后面没有依赖关系的其他指令。

  • 推测

当处理器遇到一条条件指令,比如分支,它可以推测一个分支先执行指令,虽然它并不知道这条指令是否会被执行。如果条件判断显示它的推测是正确的,处理器可以更早地得到结果。

  • 加载和存储优化

由于读写外部存储器有很长的延迟,处理器可以将多个存储指令合在一起执行,来减少传输次数。

存储类型

ARMv8架构定义了两种互斥的存储类型。所有的存储都被配置为其中的一种,即普通(Normal)和设备
(Device)。
对于所有的代码和大部分的数据区域你都可以使用普通存储。普通存储的例子包括RAM、Flash或者ROM。这类存储器能提供最高的处理器性能,因为它们是弱序的,而且在处理器上有更少的限制。处理器可以重新排序、重复和合并对普通存储的访问。
当访问存储设备可能有副作用时,你可以使用设备存储。比如读FIFO和计时器是不可重复的,每次读都会返回不同的值。向一个控制寄存器写数据可能会出发中断。它通常只用于系统的外设。设备存储在处理器上有更多的限制。如果存储设备标记为设备存储,就不能对它执行推测数据访问。
有四种设备存储类型,不同类型对应的规则也不一样。

  • Device-nGnRnE
  • Device-nGnRE
  • Device-nGRE
  • Device-GRE

后缀的字母指的是下面三种属性:

  • Gathering or non Gathering (G or nG) 这个属性决定是否可以把多次访问合成一个总线操作。
  • Re-ordering (R or nR) 这个属性决定是否可以把对这个设备的访问进行重新排序。
  • Early Write Acknowledgement (E or nE) 这个属性决定处理器和设备之间的写缓冲区是否可以发送写完成确认。

    屏障

    ARM架构中有屏障(barrier)指令可以在某个指定的点强制访问顺序和访问完成。在其他架构中,类似的指令也叫栅栏(fence)。
    屏障指令有三种:

    Instruction Synchronization Barrier (ISB)

    指令同步屏障。这个可以用来保证后续的指令都会被重新取一遍,这样访问权限就可以根据当前的MMU配置再检查一遍。这可以保证之前执行的切换上下文的操作,比如写系统控制寄存器,在ISB指令结束时都会完成。从硬件的角度看,这意味着指令流水线被清空了。

    Data Memory Barrier (DMB)

    数据存储屏障。这可以防止跨屏障指令的数据访问指令被重排序。在DMB之前这个处理器执行的所有的数据访问,即加载和存储,都被指定共享域中其他处理器可见。

    Data Synchronization Barrier (DSB)

    数据同步屏障。它和DMB一样能防止指令重排,但还有另外的作用,阻止后续指令的执行,不只是load和store,直到同步完成。
    举个例子:

    1. DC ISW, x5 // operation must have completed before DSB can complete
    2. STR x0, [x1] // Access must have completed before DSB can complete
    3. DSB ISH
    4. ADD x2, x2, #3 // Cannot be executed until DSB completes

    DMB和DSB指令后面要跟个参数,指明访问类型和共享域。可用的选项如下:
    image.png

  • Load-Load/Store

这意味着屏障结束前所有的load操作必须完成,但不要求store操作完成。程序中屏障后面的load和store必须等待屏障的结束。

  • Store-Store

这意味着这个屏障只对store其作用,屏障前后的load还可以被重新排序。

  • Any-Any

这意味着屏障结束前load和store都必须完成。程序中屏障后面的load和store必须等待屏障的结束。
屏障可以用来防止不安全的优化发生,强制特定的存储顺序。使用不必要的屏障可以降低软件的性能。