TLB緩存是個神馬鬼,如何查看TLB miss?

介紹TLB之前,我們先來回顧一個操作系統裡的基本概念,虛擬內存。

虛擬內存

在用戶的視角里,每個進程都有自己獨立的地址空間,A進程的4GB和B進程4GB是完全獨立不相關的,他們看到的都是操作系統虛擬出來的地址空間。但是呢,虛擬地址最終還是要落在實際內存的物理地址上進行操作的。操作系統就會通過頁表的機制來實現進程的虛擬地址到物理地址的翻譯工作。其中每一頁的大小都是固定的。這一段我不想介紹的太過於詳細,對這個概念不熟悉的同學回去翻一下操作系統的教材。

頁表管理有兩個關鍵點,分別是頁面大小和頁表級數

  • 頁面大小
  • 在Linux下,我們通過如下命令可以查看到當前操作系統的頁大小
# getconf PAGE_SIZE
4096

可以看到當前我的Linux機器的頁表是4KB的大小。

  • 頁表級數
  • 頁表級數越少,虛擬地址到物理地址的映射會很快,但是需要管理的頁表項會很多,能支持的地址空間也有限。
  • 相反頁表級數越多,需要的存儲的頁表數據就會越少,而且能支持到比較大的地址空間,但是虛擬地址到物理地址的映射就會越慢。

為了幫助大家回憶這段知識,我舉個例子。如果想支持32位的操作系統下的4GB進程虛擬地址空間,假設頁表大小為4K,則共有2的20次方頁面。如果採用速度最快的1級頁表,對應則需要2的20次方個頁表項。一個頁表項假如4字節,那麼一個進程就需要(1048576*4=)4M的內存來存頁表項。

如果是採用2級頁表,如圖:


TLB緩存是個神馬鬼,如何查看TLB miss?


圖1.jpg

則只需要頁目錄1024個,頁表項1024個,總共2028個頁表管理條目,(2048*4=)8k就可以支持起4GB的地址空間轉換。

更何況操作系統需要支持的可是64位地址空間,而且要支持成百上千的進程,這個開銷會大道不可忍。

所以每個操作系統制定頁表級數的時候都是在映射速度和頁表佔用空間中取折中。

Linux在v2.6.11以後,最終採用的方案是4級頁表,分別是:

- PGD:page Global directory(47-39), 頁全局目錄

- PUD:Page Upper Directory(38-30),頁上級目錄

- PMD:page middle directory(29-21),頁中間目錄

- PTE:page table entry(20-12),頁表項

這樣,一個64位的虛擬空間,就需要:2^9 個PGD + 2^9 個PUD + 2^9 個PMD + 2^9 個PTE = 2048個頁表數據結構。現在的頁表數據結構被擴展到了8byte。僅僅需要(2048*8=)16K就可以支持起(2^48 =)256T的進程地址空間。

頁錶帶來的問題

上面終於費勁扒了半天Linux虛擬內存的實現,我終於可以開始說我想說的重點了。

雖然16K的頁表數據支持起了256T的地址空間尋址。但是,這也帶來了額外的問題,頁表是存在內存裡的。那就是一次內存IO光是虛擬地址到物理地址的轉換就要去內存查4次頁表,再算上真正的內存訪問,竟然需要5次內存IO才能獲取一個內存數據!!

TLB應運而生

和CPU的L1、L2、L3的緩存思想一致,既然進行地址轉換需要的內存IO次數多,且耗時。那麼幹脆就在CPU裡把頁表儘可能地cache起來不就行了麼,所以就有了TLB(Translation Lookaside Buffer),專門用於改進虛擬地址到物理地址轉換速度的緩存。其訪問速度非常快,和寄存器相當,比L1訪問還快。

我本來想實際看一下TLB的信息,但翻遍了Linux的各種命令,也沒有找到像sysfs這麼方便查看L1、L2、L3大小的方法。僅僅提供下圖供大家參考吧! (誰要是找到了查看TLB的命令,別忘了分享給飛哥啊,謝謝!)


TLB緩存是個神馬鬼,如何查看TLB miss?


圖2.jpg

有了TLB之後,CPU訪問某個虛擬內存地址的過程如下

  • 1.CPU產生一個虛擬地址
  • 2.MMU從TLB中獲取頁表,翻譯成物理地址
  • 3.MMU把物理地址發送給L1/L2/L3/內存
  • 4.L1/L2/L3/內存將地址對應數據返回給CPU

由於第2步是類似於寄存器的訪問速度,所以如果TLB能命中,則虛擬地址到物理地址的時間開銷幾乎可以忽略。如果想了解TLB更詳細的工作機制,請參考《深入理解計算機系統-第9章虛擬內存》

工具

既然TLB緩存命中很重要,那麼有什麼工具能夠查看你的系統裡的命中率呢? 還真有

# perf stat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses -p $PID
Performance counter stats for process id '21047':
627,809 dTLB-loads
8,566 dTLB-load-misses # 1.36% of all dTLB cache hits
2,001,294 iTLB-loads

3,826 iTLB-load-misses # 0.19% of all iTLB cache hits

擴展

因為TLB並不是很大,只有4k,而且現在邏輯核又造成會有兩個進程來共享。所以可能會有cache miss的情況出現。而且一旦TLB miss造成的後果可比物理地址cache miss後果要嚴重一些,最多可能需要進行5次內存IO才行。建議你先用上面的perf工具查看一下你的程序的TLB的miss情況,如果確實不命中率很高,那麼Linux允許你使用大內存頁,很多大牛包括PHP7作者鳥哥也這樣建議。這樣將會大大減少頁表項的數量,所以自然也會降低TLB cache miss率。所要承擔的代價就是會造成一定程度的內存浪費。在Linux裡,大內存頁默認是不開啟的。

個人公眾號“開發內功修煉”,打通理論與實踐的任督二脈。

參考文獻

  • Linux內核4級頁表的演進
  • 什麼是TLB和PCID?為什麼要有PCID?為什麼Linux現在才開始使用它?
  • MMU內存管理單元


分享到:


相關文章: