ARM 架构的存储器模型
ARM 架构的存储器模型
ARM 的存储器模型是一个相对复杂但设计精密的体系,它定义了处理器如何与内存进行交互,包括内存访问的顺序、可见性以及缓存行为等。这对于理解多核编程、并发控制和底层系统性能至关重要。
ARM 架构,特别是 ARMv8 及以后的版本,采用了一种称为 弱一致性内存模型(Weakly Ordered Memory Model) 的设计。
核心特征:弱一致性内存模型
与 x86 的 强一致性内存模型(Strongly Ordered Memory Model) 相比,ARM 的弱序模型有以下几个关键特点:
-
允许重排序:为了极致的性能,处理器和编译器可以对内存访问指令进行大量的重排序(Reordering),只要在单线程上下文中最终结果正确即可。
- 写-写重排序:两个写操作可以以不同于程序顺序的顺序提交到内存。
- 读-读重排序:两个读操作可以以不同的顺序执行。
- 读-写重排序:读操作可能会被重排到写操作之前,写操作也可能会被重排到读操作之前。
- 注意:存在数据依赖关系的操作(例如,先读地址 A,再向地址 A 写)不会被重排序,否则会破坏程序的正确性。
-
多核系统中的可见性:在一个核心上执行的内存写操作,不会立即被其他核心看到。写入的值会先存在于该核心的写缓冲区(Store Buffer)中,然后才异步地刷新到共享缓存和主内存中。这意味着,不同核心看待内存的“顺序”可能是不一致的。
为什么需要内存屏障?
由于存在重排序和可见性延迟,在多线程编程中,如果没有任何约束,程序的行为将是不可预测的。为了解决这个问题,ARM 提供了一组 内存屏障指令(Memory Barrier Instructions),让程序员和编译器能够强制约束内存访问的顺序和可见性。
内存屏障就像一道“栅栏”,确保在屏障之前的所有内存操作(在某种意义上)完成后,才执行屏障之后的内存操作。
ARMv8 架构主要提供了以下三种内存屏障指令:
-
数据存储屏障(Data Memory Barrier, DMB)
-
作用:确保在 DMB 指令之前的所有内存访问(读和写)都完成后,才执行其之后的内存访问。
-
使用场景:主要用于保证对内存的访问顺序。例如,在更新一个数据结构指针之前,确保所有对数据结构本身的写入都已经完成(对其它核心可见)。
-
举例:
STR X0, [X1] ; 将数据写入数据结构(将寄存器 X0 中的值存储到以寄存器 X1 中的值为地址的内存位置) DMB ISH ; 等待上面的写入对所有核心可见 STR X1, [X2] ; 更新指针,指向新的数据结构 ; 如果没有 DMB,其他核心可能看到一个更新了的指针,但指向的却是旧数据
-
-
数据同步屏障(Data Synchronization Barrier, DSB)
- 作用:比 DMB 更严格。它确保在 DSB 指令之前的所有内存访问和高速缓存、分支预测、TLB 维护等所有指令都完成后,才执行其之后的任何指令(不仅仅是内存访问)。
- 使用场景:当你需要确保内存操作完全完成后才能做下一件事时。例如,在配置完内存映射寄存器(如 MMU)后,必须使用 DSB 来确保配置生效,然后才能继续执行后续指令。
-
指令同步屏障(Instruction Synchronization Barrier, ISB)
- 作用:它会刷新处理器的流水线(pipeline),确保在 ISB 之后的所有指令都从缓存或内存中重新预取。这意味着在 ISB 之前的所有上下文更改(如系统寄存器的修改)对后续指令完全可见。
- 使用场景:在修改了会影响指令行为的系统控制寄存器(如 MMU、FPU 的设置)后,必须使用 ISB 来确保后续指令使用新的设置被执行。
简单类比:
- DMB:像是一个仓库管理员,确保所有货物(数据)都按顺序摆上货架(内存)后,才允许贴出新的库存清单(指针)。
- DSB:不仅是管理员,他还要求所有记账、清点工作都完成后,才允许你离开仓库去做别的事。
- ISB:是要求所有工人都忘掉旧的工作方式,重新阅读新的工作手册后再开始干活。
共享域(Shareability Domains)
ARM 架构还有一个重要的概念叫“共享域”,它定义了内存屏障操作的范围。在多集群(Multi-Cluster)设计中,并非所有核心都能直接看到彼此的内存操作。屏障指令可以指定其作用的域:
- ISH:Inner Shareable Domain。最常见的选择,通常包括一个芯片上的所有核心。
- NSH:Non-shareable。仅针对本处理器。
- OSH:Outer Shareable Domain。包括可能不在同一个芯片上的处理器(如 big.LITTLE 集群之间)。
- SY:Full System。包括所有组件,如 GPU、DMA 等。
例如,DMB ISH
表示屏障在“内部可共享域”内生效。
与高级语言的关系
作为应用程序开发者,你通常不会直接使用这些汇编指令。高级编程语言(如 C++11、Java)的并发原语(如 std::atomic
、volatile
(有特定语义时)、锁mutex
)在编译时,编译器会根据目标架构(如 ARM)插入正确的内存屏障指令。
例如,在 C++ 中,std::atomic<int>
的存储(store)操作默认使用 memory_order_seq_cst
排序,编译器在 ARM 上会为其生成类似 DMB ISH
的指令来保证强顺序一致性。
总结
特性 | ARM (弱序模型) | x86 (强序模型) |
---|---|---|
核心特点 | 允许大量重排序以提升性能 | 基本保持程序顺序,重排序较少 |
硬件要求 | 需要显式使用内存屏障来保证顺序 | 大部分情况下硬件自动保证顺序,屏障开销小 |
性能 | 更高效,能更好地利用流水线和缓存 | 相对更保守,但编程模型更简单 |
编程复杂性 | 底层编程时需要开发者更深入地理解内存模型 | 底层编程时心智负担更轻 |
记住关键点:ARM 为了功耗和性能优势,采用了弱一致性内存模型。这意味着在多核编程中,不能依赖指令的自然顺序来保证同步,必须正确使用内存屏障(或依赖高级语言中正确的同步原语)来强制内存访问的顺序和可见性,从而编写出正确的并发程序。