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 為了功耗和性能優勢,采用了弱一致性內存模型。這意味著在多核編程中,不能依賴指令的自然順序來保證同步,必須正確使用內存屏障(或依賴高級語言中正確的同步原語)來強制內存訪問的順序和可見性,從而編寫出正確的并發程序。