3.3.3. 寫入行為
在我們開始研究在多執行環境(execution context)(執行緒或行程)使用相同記憶體的快取行為之前,我們必須先探究一個快取實作的細節。快取是假定為一致的(coherent),而且對使用者層級的程式而言,這個一致性是假定為完全透明的。系統核心程式是不同的情況;它偶爾會要求快取沖出(flush)。
這具體意味著,假如一個快取行被修改了,在這個時間點之後,對系統而言的結果與根本沒有快取、並且是主記憶體位置本身被修改的情況是相同的。這能以兩種方式或策略來實行:
- 直寫式(write-through)快取實作;
- 回寫式(write-back)快取實作。
直寫式快取是最簡單的快取一致性的實行方式。若是快取行被寫入的話,處理器也會立即將快取行寫到主記憶體中。這保證了主記憶體與快取永遠保持一致。能夠在任何快取行被取代的時候直接丟棄快取的內容。這個快取策略很簡單,但並不是非常快。舉例來說,一支不斷地修改一個區域變數的程式會在 FSB 產生大量的流量,儘管資料很可能不會在別處用到、而且可能只會短暫存在。
回寫式策略更為複雜。這時處理器不會立即將被修改的快取行寫回到主記憶體裡。取而代之地,快取行只會被標記為髒的。當快取行在未來的某個時間點從快取被丟棄時,髒位(dirty bit)將會在這時通知處理器去把資料寫回去,而不是直接丟棄內容。
寫回式快取有機會做得好非常多,這即是大多有著像樣處理器的系統中,記憶體都會以這種方式快取的原因了。處理器甚至能在快取行必須被清除之前,利用 FSB 的閒置容量來儲存快取行的內容。這使得髒位被清除,並且在需要快取中的空間的時候,處理器能夠直接丟棄這個快取行。
但回寫式實作也有個重大的問題。當有多於一個處理器(或是核心或超執行緒),並且存取到同樣的記憶體時,它仍舊必須保證每個處理器看到的一直都是相同的記憶體內容。假如一個快取行在一個處理器上是髒的(也就是說,它還沒被寫回去),並且第二個處理器試著讀取相同的記憶體位置,這個讀取操作就不能直接送到主記憶體去。而是需要第一個處理器的快取行的內容。在下一節,我們將會看到這在當前是如何實作的。
在此之前,還有兩種快取策略要提一下:
- 合併寫入(write-combining);以及
- 不可快取(uncacheable)
這兩種策略都是用在位址空間中、並非被真正的 RAM 所支援的特殊區域。系統核心為這些位址範圍設置了這些策略(在使用了記憶體型態範圍暫存器〔Memory Type Range Register,MTRR〕的 x86 處理器上),剩下的部分自動地進行。MTRR 也能用於在直寫式與回寫式策略之間選擇。
合併寫入是一種受限的快取最佳化,更常用於顯示卡一類裝置上的 RAM。由於對裝置來說,傳輸成本比區域 RAM 存取的成本還高得多,因此避免過多的傳輸是更為重要的。僅因為快取行中的一個字組被修改,就傳輸一整個快取行,在下一個操作修改了下一個字組的情況下是很浪費的。能夠輕易地想像,一種常見的情況是,表示螢幕上水平相鄰的像素點的記憶體,在多數情況下也是相鄰的。如同名字所暗示的,合併寫入會在快取行被寫出去之前合併多個寫入存取。在理想情況下,快取行會被一個字組接著一個字組地修改,並且只有在寫入最後一個字組之後,快取行才會被寫到裝置中。這能夠顯著地加速裝置對 RAM 的存取。
最後,不可快取的記憶體。這通常表示記憶體位置根本不被 RAM 所支援。它可能是一個被寫死的特殊位址,以擁有某個在 CPU 外部實作的功能。對商用硬體來說,最常見的例子是記憶體對映的(memory-mapped)位址範圍,轉譯為對附屬於匯流排上的擴充卡以及裝置(PCIe 等等)的存取。在嵌入式單板(embedded board)上,有時候會發現能夠用來開關 LED 的記憶體位址。快取這類位址顯然是個壞點子。在這種情境下的 LED 是用以除錯或者狀態回報的,會想要儘可能快地看到它。在 PCIe 擴充卡上的記憶體能夠在不與 CPU 互動的情況下改變,因此這種記憶體不應該被快取。