原文
https://blog.csdn.net/maokelong95/article/details/81362837?utm_source=blogxgwz5
[NVM Programming] A Brief Guidance: How to Issue CLFLUSH, CLFLUSHOPT,CLWB, NTSTORE, LFENCE, MFENCE, SFENCE .etc via compilers’ intrinsic functions
日志:
- 2019-04-15: 新增指令順序約束表;
- 2019-01-12: 更新指令支持現狀;
- 2018-08-02: 提交了第一版;
1. 案例速覽
1.1. 以使用 CLFLUSH 為例
- 編碼(文件名為
clflush_demo.c
)#include <x86intrin.h> // GCC 內置函數集(針對 x86 平台)
int main(int argc, char const *argv[]) {
int data = 5;
_mm_clflush(&data); // GCC 內置的 clflush
return 0;
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 編譯
gcc clflush_demo.c -msse2 # 如何確定 -m* 請參閱第二章
- 1
- 運行
1.2. 以使用 CLFLUSHOPT 為例
- 編碼(文件名為
clflushopt_demo.c
)#include <x86intrin.h> // GCC 內置函數集(針對 x86 平台)
int main(int argc, char const *argv[]) {
int data = 5;
_mm_clflushopt(&data); // GCC 內置的 clflushopt
return 0;
} - 編譯
gcc clflushopt_demo.c -mclflushopt # 如何確定 -m* 請參閱第二章
- 運行
2. 你可能想問的問題
-
如果不使用
x86intrin.h
頭文件會有什么影響?
將提醒函數未聲明,如:warning: implicit declaration of function ‘_mm_clflushopt’ [-Wimplicit-function-declaration]; -
如果在編譯時不使用 -m* 會有什么影響?
該選項告訴編譯器當前使用了哪些處理器擴展指令,如果不指定將無法通過編譯,如:error: inlining failed in call to always_inline ‘_mm_clflushopt’: target specific option mismatch -
如何確定 -m* 中的 * ?
總結流程如下:- 打開 Intel Intrinsic Guide,並檢索目標指令;如,檢索 CLFLUSH;
- 找到指令對應的函數;如,CLFLUSH 對應函數名為
_mm_clflush
; - 確定指令對應指令集擴展,可通過條目詳情頁的 “CPUID FLAGs” 或通過顏色比對;如 CLFLUSH 隸屬 “SSE2”,而 CLFLUSHOPT 這種放在 Other 里的就是自己的指令名了;
- * 即指令集擴展名;
-
編譯時報錯,說,error: unrecognized command line option “-mclflushopt”,是為什么?
可能是因為編譯器版本太低,我試過 5.4 和 7.3 版的 GCC,編譯均通過; -
運行時出錯,說,Illegal instruction (core dumped),是為什么?
你的處理器架構不支持該指令。
3. 常用非易失內存編程指令介紹
Note:
- 支持現狀的格式為 Processor Generation Introduction. (Supported in Microarchitecture);數據獲取自 Intel® Architecture Instruction Set Extensions and Future Features Programming Reference。
- TBD = To Be Discussed,未固定。
-
CLFLUSH。CLFLUSH(Cache Line Flush,緩存行刷回)能夠把指定緩存行(Cache Line)從所有級緩存中淘汰,若該緩存行中的數據被修改過,則將該數據寫入主存;支持現狀:目前主流處理器均支持該指令。
-
CLFLUSHOPT。CLFLUSHOPT(Optimized CLFLUSH,優化的緩存行刷回)作用與 CLFLUSH 相似,但其之間的指令級並行度更高,比如在訪問不同 CacheLine 時,CLFLUHOPT 可以亂序執行。支持現狀:
- Intel® Xeon® Processor Scalable Family. (Skylake Server and later)
- 6th Generation Intel® Core™ processor family. (Skylake and later)
- Intel® Atom™ processor. (Goldmont and later)
-
CLWB。CLWB(Cache Line Write Back,緩存行寫回)作用與 CLFLUSHOPT 相似,但在將緩存行中的數據寫回之后,該緩存行仍將呈現為未被修改過的狀態;支持現狀:
- Intel® Xeon® Processor Scalable Family. (Skylake Server and later)
- TBD. (Ice Lake and later)
- TBD. (Future Tremont and later)
-
NT STORES。NT STORES(NonTemporal stores) 是一系列用於存儲不同字長數據的指令,其包括 MOVNTDQ 等。NT Stores 指令在傳輸數據時能夠繞過緩存,而直接將數據寫入主存。
-
PCOMMIT。已經棄用。該指令用於將已經通過前述指令刷到內存控制器的數據提交到主存,該指令現因強制要求所有平台實現 ADR 特性而不再具有使用價值,從而被廢棄。所謂 ADR(Asynchronous DRAM Refresh,異步 DRAM 刷新)特性原本為 DRAM-based NVDIMM 設計,其通過大電容和特定時序來確保掉電后內存控制器及部分緩存中的數據順利寫入非易失內存。
-
FENCE。FENCE 指令,也稱內存屏障(Memory Barrier),起着約束其前后訪存指令之間相對順序的作用。其包括 LFENCE(約束 Load 指令), MFENCE(約束 L/S 指令), SFENCE(約束 Store 指令)。希望從更深層次去理解這個指令的意義,可以翻翻我之前的博客:內存模型系列(上)- 內存一致性模型(Memory Consistency),其對應 Safety Net 部分。
注:以上指令均為 X86 指令,arm 處理器的指令集我未調研過,因此此處不作介紹。
3. 常用非易失內存編程指令順序約束表
我們知道,Out-of-Order 處理器可能會亂序執行指令以盡可能增大地處理器的指令吞吐率。但這種 Re-order 可能並不是我們所期望的。我們唯有充分了解這些非易失編程指令的順序約束,才能做到完全把控處理器的訪存行為。
為了清晰地將指令間的順序約束表現出來,這里草擬了一張介紹指令順序約束的表格。其中每行表示一個是否與之排序的項目,而每列表示一條非易失編程指令。
序項\指令 | CLFLUSH | CLFLUSHOPT | CLWB | MOVNT1 |
---|---|---|---|---|
CLFLUSH | Y | N | N | ? |
CLFLUSHOPT | N | N | N | ? |
CLWB | ? | ? | N | ? |
MOVNT | ? | ? | ? | N |
writes | Y | N | N | N |
Locked RMW | Y | Y | Y | Y |
fence | Y | Y | Y | Y |
注意:
- 對同一 cacheline 操作時仍將排序,但 MOVNT 未見相關描述;
- ? 部分表示已有資料中未見相關描述;
- 上面的 fence 指的是 store-fencing operations,包括 SFENCE 及 MFENCE;
- 上面的 Locked RMW 包括 XCHG 及 LOCK-prefixed instructions,詳見 內存模型系列(上)- 內存一致性模型(Memory Consistency)。
Intel® 64 and IA-32 Architectures Optimization Reference Manual. 7.4.1 The Non-temporal Store Instructions. ↩︎
</div>