執行人: I-Ying-Tsai
細讀並行程式設計: Atomics 操作、紀錄認知和提問,務必涵蓋「Memory Ordering 和 Barrier」
利用 litmus7 在 Raspberry Pi 4B 硬體實驗並充分解讀
適度改進筆記內容
搭配閱讀 2024 年報告-1 及 2024 年報告-2
做實驗之前我需要先了解我的硬體架構:
架構如下:
首先安裝工具:
我根據 Paul McKenney 在 演講中所使用的簡報 使用以下指令先安裝了 herd7tools :
接著我先使用了 litmus7 工具分析了 SB.litmus :
SB.litmus :
對它執行 litmus7 ./SB.litmus -carch AArch64
指令後結果如下:
在實驗之前,我透過閱讀老師的 Atomics 操作的教材以及借助詢問 GPT 給我的一些破碎的資訊,先得到了一些概念並試著翻閱手冊,思考問題後再進行實驗:
Cache coherence : 翻閱 Arm 關於 Cache coherence 的手冊後,發現手段有三種:
Disable caching:這個很直觀,它會把要共享的記憶體區域標記成 non-cacheable。但這會讓DRAM 延遲大幅提高、功耗高、效能降低。也就是會讓 Cache 的優勢消失。在這裡不會被實做。
Software managed coherency:
對於這個方法,Arm 官方是這麼描述的:
Software managed coherency is the traditional solution to the data sharing problem. Here the software, usually device drivers, must clean or flush dirty data from caches, and invalidate old data to enable sharing with other processors or masters in the system. This takes processor cycles, bus bandwidth, and power.
於是我寫了一個 Kernel Module 搭配 dma 來試著使用它:
Hardware managed coherency:
這個是目前大部分的處理器採用的方式,而 ARM Cortex-A72 採用的是基於 MOESI protocol 的 Cache 行為,具體行為如下:
並基於這個協定讓每個 CPU 使用 Snoop Control Unit (SCU) 監控各個 CPU 的 Cacheline 狀態。(當某 CPU 要讀一條 Cacheline 時,SCU 會 snoop 其他 CPU,看是否有更新版本),接著各 CPU 之間透過 ARM 定義的 AMBA ACE bus (AMBA Coherent Extensions) 傳輸 snoop transaction:
Store Buffering
ARM TRM 裡提到:
The Load-Store Unit contains multiple queues, including a store buffer. This enables stores to retire from the pipeline before they are globally visible, improving throughput.
Q:那 Cortex-A72 pipeline 是如何設計的呢?
A:被分成這幾個步驟:
Instruction Fetch:從 L1 I-Cache 抓取指令,每 cycle 可供應最多 3 條指令。
Instruction Decode:參考原文
The decode unit translates fetched instructions into internal operations that can be dispatched out of order to execution pipelines. It includes branch decode, register rename, and allocation logic.
Dispatch / Rename:它會實際地執行 rename ,具體行為如下
讀取 free list 中可用的 physical registers
free list : 它會記錄「目前哪些 Physical Registers 是空閒的,可以被新的指令使用」。
這裡可能是用 bit vector + priority encoder 來實做,因為我沒有在手冊裡翻到對 free list 的實做細節描述,但在閱讀其他開源專案時發現幾乎都是使用了這個實做方式其變形。
Ex:Berkeley Out-of-Order Machine
Q:為何 AR 和 PR 需要分開設計?我直覺想到說 AR 應該直接照著 PR 來定義。
A:為了讓 Pipeline 可以支援 Reordering 與 speculative execution。首先因為 PR 的數量比 AR 還多,如果 CPU 內部只有 Architectural Register, Pipeline 中同一
一個 AR 只能有一份值。但若有兩條指令要對同一個 AR 寫入:
或是 WAR:
在第一個範例中,第二條指令必須等到第一條指令將值寫回 AR,才能開始執行,否則會發生 harzard。
在第二個範例中,I2 可能因為 reordering 而導致先執行,直接把 R2 改成 7。
將每個 architectural register 對應到一個新的 physical register
更新 rename table
配給 Reorder Buffer (ROB) Entry,建立依賴鏈
將 rename 後的 µops 放進 Issue Queue。
首先觀察上面的測試結果,發現這段程式理論上只會出現 (1,1) ,也就是 Sequential Consistency ,但顯然結果並非如此,首先因為 Cache 的存在,
接著,我下載了 Linux kernel 的 tools/memory-model
子目錄,接著進入 tools/memory-model
資料夾,以 herd 執行其中的範例測試並試著分析結果:
MP+poonceonces.litmus
:WRITE_ONCE
和 READ_ONCE
,保證了讀和寫是一個 atomic 操作,compiler 不會將它重排。根據這段程式碼,理論上它只會有 3 種情況:
然而當我用 herd 進行分析後的執行結果卻是:
第四種可能出現了!根據這個執行結果我們幾乎可以 100% 確定 CPU
有可能重排了這段程式碼,於是我是著去試著查看 Arm 手冊 來試著了解 ARMv8-A 架構下 memory model 對 weak ordering 的容忍程度後發現:
load-load
, load-store
, store-store
, store-load
都可能被 CPU pipeline 重排。接著我試著使用了 litmus7
分析在 AArch64 架構下的實驗結果:
首先我用以下指令試著利用 herd 工具包裡的 litmus.exe
將 MP+poonceonces.litmus
轉成 .c
但出現了:
我檢查了 util.c/h 發現是空的,於是我手動將這兩個巨集加進了 MP+poonceonces.c
:
結果編譯成功:
至少該涵蓋以下演講: (包含錄影)