4路组相连cache设计_Cache组织方式
經過下面文章的介紹,我們已經知道cache的基本工作原理。
smcdef:圖說Cache - Cache的基本原理?zhuanlan.zhihu.com但是,我們一直避開了一個關鍵問題。我們都知道cache控制器根據地址查找判斷是否命中,這里的地址究竟是虛擬地址(virtual address,VA)還是物理地址(physical address,PA)?我們應該清楚CPU發出對某個地址的數據訪問,這個地址其實是虛擬地址,虛擬地址經過MMU轉換成物理地址,最終從這個物理地址讀取數據。因此cache的硬件設計既可以采用虛擬地址也可以采用物理地址甚至是取兩者地址部分組合作為查找cache的依據。
虛擬高速緩存(VIVT)
我們首先介紹的是虛擬高速緩存,這種cache硬件設計簡單。在cache誕生之初,大部分的處理器都使用這種方式。虛擬高速緩存以虛擬地址作為查找對象。如下圖所示。
虛擬地址直接送到cache控制器,如果cache hit。直接從cache中返回數據給CPU。如果cache miss,則把虛擬地址發往MMU,經過MMU轉換成物理地址,根據物理地址從主存(main memory)讀取數據。由于我們根據虛擬地址查找高速緩存,所以我們是用虛擬地址中部分位域作為索引(index),找到對應的的cacheline。然后根據虛擬地址中部分位域作為標記(tag)來判斷cache是否命中。因此,我們針對這種index和tag都取自虛擬地址的高速緩存稱為虛擬高速緩存,簡稱VIVT(Virtually Indexed Virtually Tagged)。另外,我們復習下cache控制器查找數據以及判斷是否命中的規則:通過index查找對應的cacheline,通過tag判斷是否命中cache。 虛擬高速緩存的優點是不需要每次讀取或者寫入操作的時候把虛擬地址經過MMU轉換為物理地址,這在一定的程度上提升了訪問cache的速度,畢竟MMU轉換虛擬地址需要時間。同時硬件設計也更加簡單。但是,正是使用了虛擬地址作為tag,所以引入很多軟件使用上的問題。 操作系統在管理高速緩存正確工作的過程中,主要會面臨兩個問題。歧義(ambiguity)和別名(alias)。為了保證系統的正確工作,操作系統負責避免出現歧義和別名。
歧義(ambiguity)
歧義是指不同的數據在cache中具有相同的tag和index。cache控制器判斷是否命中cache的依據就是tag和index,因此這種情況下,cache控制器根本沒辦法區分不同的數據。這就產生了歧義。什么情況下發生歧義呢?我們知道不同的物理地址存儲不同的數據,只要相同的虛擬地址映射不同的物理地址就會出現歧義。例如兩個互不相干的進程,就可能出現相同的虛擬地址映射不同的物理地址。假設A進程虛擬地址0x4000映射物理地址0x2000。B進程虛擬地址0x4000映射物理地址0x3000。當A進程運行時,訪問0x4000地址會將物理地址0x2000的數據加載到cacheline中。當A進程切換到B進程的時候,B進程訪問0x4000會怎樣?當然是會cache hit,此時B進程就訪問了錯誤的數據,B進程本來想得到物理地址0x3000對應的數據,但是卻由于cache hit得到了物理地址0x2000的數據。操作系統如何避免歧義的發生呢?當我們切換進程的時候,可以選擇flush所有的cache。flush cache操作有兩種: - 使主存儲器有效。針對write back高速緩存,首先應該使主存儲器有效,保證已經修改數據的cacheline寫回主存儲器,避免修改的數據丟失。 - 使高速緩存無效。保證切換后的進程不會錯誤的命中上一個進程的緩存數據。
因此,切換后的進程剛開始執行的時候,將會由于大量的cache miss導致性能損失。所以,VIVT高速緩存明顯的缺點之一就是經常需要flush cache以保證歧義不會發生,最終導致性能的損失。VIVT高速緩存除了面對歧義問題外,還面臨另一個問題:別名(alias)。
別名(alias)
當不同的虛擬地址映射相同的物理地址,而這些虛擬地址的index不同,此時就發生了別名現象(多個虛擬地址被稱為別名)。通俗點來說就是指同一個物理地址的數據被加載到不同的cacheline中就會出現別名現象。 考慮這樣的一個例子。虛擬地址0x2000和0x4000都映射到相同的物理地址0x8000。這意味著進程既可以從0x2000讀取數據,也能從地址0x4000讀取數據。假設系統使用的是直接映射VIVT高速緩存,cache更新策略采用寫回機制,并且使用虛擬地址的位<15...4>作為index。那么虛擬地址0x2000和虛擬地址0x4000的index分別是0x200和0x400。這意味同一個物理地址的數據會加載到不同的cacheline。假設物理地址0x8000存儲的數據是0x1234。程序先訪問0x2000把數據0x1234加載到第0x200(index)行cacheline中。接著訪問0x4000,會將0x1234再一次的加載到第0x400(index)行cacheline中。現在程序將0x2000地址數據修改成0x5678。由于采用的是寫回策略,因此修改的數據依然躺在cacheline中。當程序訪問0x4000的時候由于cache hit導致讀取到舊的數據0x1234。這就造成了數據不一致現象,這不是我們想要的結果。可以選擇下面的方法避免這個問題。
針對共享數據所在頁的映射方式采用nocache映射。例如上面的例子中,0x2000和0x4000映射物理地址0x8000的時候都采用nocache的方式,這樣不通過cache的訪問,肯定可以避免這種問題。但是這樣就損失了cache帶來的性能好處。這種方法既適用于不同進程共享數據,也適用于同一個進程共享數據。 如果是不同進程之間共享數據,還可以在進程切換時主動flush cache(使主存儲器有效和使高速緩存無效)的方式避免別名現象。但是,如果是同一個進程共享數據該怎么辦?除了nocache映射之外,還可以有另一種解決方案。這種方法只針對直接映射高速緩存,并且使用了寫分配機制有效。在建立共享數據映射時,保證每次分配的虛擬地址都索引到相同的cacheline。這種方式,后面還會重點說。
物理高速緩存(PIPT)
基于對VIVT高速緩存的認識,我們知道VIVT高速緩存存在歧義和名別兩大問題。主要問題原因是:tag取自虛擬地址導致歧義,index取自虛擬地址導致別名。所以,如果想讓操作系統少操心,最簡單的方法是tag和index都取自物理地址。物理的地址tag部分是獨一無二的,因此肯定不會導致歧義。而針對同一個物理地址,index也是唯一的,因此加載到cache中也是唯一的cacheline,所以也不會存在別名。我們稱這種cache為物理高速緩存,簡稱PIPT(Physically Indexed Physically Tagged)。PIPT工作原理如下圖所示。
CPU發出的虛擬地址經過MMU轉換成物理地址,物理地址發往cache控制器查找確認是否命中cache。雖然PIPT方式在軟件層面基本不需要維護,但是硬件設計上比VIVT復雜很多。因此硬件成本也更高。同時,由于虛擬地址每次都要翻譯成物理地址,因此在查找性能上沒有VIVT方式簡潔高效,畢竟PIPT方式需要等待虛擬地址轉換物理地址完成后才能去查找cache。順便提一下,為了加快MMU翻譯虛擬地址的速度,硬件上也會加入一塊cache,作用是緩存虛擬地址和物理地址的映射關系,這塊cache稱之為TLB(Translation Lookaside Buffer)。當MMU需要轉換虛擬地址時,首先從TLB中查找,如果cache hit,則直接返回物理地址。如果cache miss則需要MMU查找頁表。這樣就加快了虛擬地址轉換物理地址的速度。如果系統采用的PIPT的cache,那么軟件層面基本不需要任何的維護就可以避免歧義和別名問題。這是PIPT最大的優點。現在的CPU很多都是采用PIPT高速緩存設計。在Linux內核中,可以看到針對PIPT高速緩存的管理函數都是空函數,無需任何的管理。
物理標記的虛擬高速緩存(VIPT)
為了提升cache查找性能,我們不想等到虛擬地址轉換物理地址完成后才能查找cache。因此,我們可以使用虛擬地址對應的index位查找cache,與此同時(硬件上同時進行)將虛擬地址發到MMU轉換成物理地址。當MMU轉換完成,同時cache控制器也查找完成,此時比較cacheline對應的tag和物理地址tag域,以此判斷是否命中cache。我們稱這種高速緩存為VIPT(Virtually Indexed Physically Tagged)。
VIPT以物理地址部分位作為tag,因此我們不會存在歧義問題。但是,采用虛擬地址作為index,所以可能依然存在別名問題。是否存在別名問題,需要考慮cache的結構,我們需要分情況考慮。
VIPT Cache為什么不存在歧義
在這里重點介紹下為什么VIPT Cache不存在歧義。假設以32位CPU為例,頁表映射最小單位是4KB。我們假設虛擬地址<12:4>位(這是一個有別名問題的VIPT Cache)作為index,于此同時將虛擬地址<31:12>發送到MMU轉換得到物理地址的<31:12>,這里我們把<31:12>作為tag,并不是<31:13>。這地方很關鍵,也就是說VIPT的tag取決于物理頁大小的剩余位數,而不是去掉index和offset的剩余位數。物理tag是惟一的,所以不存在歧義。
VIPT Cache什么情況不存在別名
我們知道VIPT的優點是查找cache和MMU轉換虛擬地址同時進行,所以性能上有所提升。歧義問題雖然不存在了,但是別名問題依舊可能存在,那么什么情況下別名問題不會存在呢?Linux系統中映射最小的單位是頁,一頁大小是4KB。那么意味著虛擬地址和其映射的物理地址的位<11...0>是一樣的。針對直接映射高速緩存,如果cache的size小于等于4KB,是否就意味著無論使用虛擬地址還是物理地址的低位查找cache結果都是一樣呢?是的,因為虛擬地址和物理地址對應的index是一樣的。這種情況,VIPT實際上相當于PIPT,軟件維護上和PIPT一樣。如果示例是一個四路組相連高速緩存呢?只要滿足一路的cache的大小小于等于4KB,那么PIPT方式的cache也不會出現別名問題。
VIPT Cache的別名問題
假設系統使用的是直接映射高速緩存,cache大小是8KB,cacheline大小是256字節。這種情況下的VIPT就存在別名問題。因為index來自虛擬地址位<12...8>,虛擬地址和物理地址的位<11...8>是一樣的,但是bit12卻不一定相等。 假設虛擬地址0x0000和虛擬地址0x1000都映射相同的物理地址0x4000。那么程序讀取0x0000時,系統將會從物理地址0x4000的數據加載到第0x00行cacheline。然后程序讀取0x1000數據,再次把物理地址0x4000的數據加載到第0x10行cacheline。這不,別名出現了。相同物理地址的數據被加載到不同cacheline中。
如何解決VIPT Cache別名問題
我們接著上面的例子說明。首先出現問題的場景是共享映射,也就是多個虛擬地址映射同一個物理地址才可能出現問題。我們需要想辦法避免相同的物理地址數據加載到不同的cacheline中。如何做到呢?那我們就避免上個例子中0x1000映射0x4000的情況發生。我們可以將虛擬地址0x2000映射到物理地址0x4000,而不是用虛擬地址0x1000。0x2000對應第0x00行cacheline,這樣就避免了別名現象出現。因此,在建立共享映射的時候,返回的虛擬地址都是按照cache大小對齊的地址,這樣就沒問題了。如果是多路組相連高速緩存的話,返回的虛擬地址必須是滿足一路cache大小對齊。在Linux的實現中,就是通過這種方法解決別名問題。
不存在的PIVT高速緩存
按照排列組合來說,應該還存在一種PIVT方式的高速緩存。因為PIVT沒有任何優點,卻包含以上的所有缺點。你想想,PIVT方式首先要通過MMU轉換成物理地址,然后才能根據物理地址index域查找cache。這在速度上沒有任何優勢,而且還存在歧義和別名問題。請忘記它吧。不,應該不算是忘記,因為它從來就沒出現過。
總結
VIVT Cache問題太多,軟件維護成本過高,是最難管理的高速緩存。所以現在基本只存在歷史的文章中。現在我們基本看不到硬件還在使用這種方式的cache。現在使用的方式是PIPT或者VIPT。如果多路組相連高速緩存的一路的大小小于等于4KB,一般硬件采用VIPT方式,因為這樣相當于PIPT,豈不美哉。當然,如果一路大小大于4KB,一般采用PIPT方式,也不排除VIPT方式,這就需要操作系統多操點心了。
總結
以上是生活随笔為你收集整理的4路组相连cache设计_Cache组织方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python爬取岗位数据并分析_区块链岗
- 下一篇: opencv 图像增强_图像增强、锐化,