11. TLB¶
當核心取消對映或修改某個記憶體範圍的屬性時,它有兩個選擇
使用兩條指令序列重新整理整個TLB。這是一個快速操作,但會造成附帶損害:來自我們要重新整理的區域以外的TLB條目將被銷燬,並且必須在稍後重新填充,這會產生一定的成本。
使用 invlpg 指令一次使單個頁面失效。這可能需要更多的指令,但它是一種更精確的操作,不會對其他TLB條目造成附帶損害。
採用哪種方法取決於以下幾點
正在執行的重新整理的大小。顯然,重新整理整個地址空間最好透過重新整理整個TLB來完成,而不是執行2^48/PAGE_SIZE次單獨的重新整理。
TLB的內容。如果TLB是空的,那麼執行全域性重新整理不會造成附帶損害,並且所有單獨的重新整理最終都會是浪費的工作。
TLB的大小。TLB越大,我們用完全重新整理造成的附帶損害就越多。因此,TLB越大,單獨重新整理看起來就越有吸引力。資料和指令有單獨的TLB,不同的頁面大小也是如此。
微架構。TLB已成為現代CPU上的多級快取,並且相對於單頁重新整理,全域性重新整理變得更加昂貴。
顯然,核心無法知道所有這些事情,尤其是在給定的重新整理期間TLB的內容。重新整理的大小也會因工作負載而異。基本上沒有“正確”的選擇點。
如果您看到 invlpg 指令(或指令_near_它)在高配置中顯示出來,則可能執行了過多的單獨失效。如果您認為單獨的失效被呼叫得太頻繁,您可以降低可調引數
/sys/kernel/debug/x86/tlb_single_page_flush_ceiling
這將導致我們在更多情況下執行全域性重新整理。將其降低到0將停用單獨重新整理的使用。將其設定為1是一個非常保守的設定,在正常情況下絕不需要將其設定為0。
儘管事實上,x86上的單個單獨重新整理保證重新整理完整的2MB [1],但hugetlbfs始終使用完全重新整理。THP的處理方式與普通記憶體完全相同。
您可能會在 flush_tlb_mm_range() 中看到 invlpg 出現在 profiles 中,或者您可以使用 trace_tlb_flush() 跟蹤點來確定重新整理操作花費的時間。
本質上,您是在平衡花費在執行 invlpg 上的週期與以後花費在重新填充 TLB 上的週期。
您可以使用效能計數器和 “perf stat” 來衡量 TLB 重新填充的開銷,如下所示
perf stat -e
cpu/event=0x8,umask=0x84,name=dtlb_load_misses_walk_duration/,
cpu/event=0x8,umask=0x82,name=dtlb_load_misses_walk_completed/,
cpu/event=0x49,umask=0x4,name=dtlb_store_misses_walk_duration/,
cpu/event=0x49,umask=0x2,name=dtlb_store_misses_walk_completed/,
cpu/event=0x85,umask=0x4,name=itlb_misses_walk_duration/,
cpu/event=0x85,umask=0x2,name=itlb_misses_walk_completed/
這適用於 IvyBridge 時代的 CPU (i5-3320M)。不同的 CPU 可能有不同名稱的計數器,但它們至少應該以某種形式存在。您可以使用 pmu-tools ‘ocperf list’ (https://github.com/andikleen/pmu-tools) 來查詢給定 CPU 的正確計數器。