5.2.作業系統對 NUMA 的支援
為了支援 NUMA 機器,作業系統必須將記憶體分散式的性質納入考量。舉例來說,若是一個行程執行在一個給定的處理器上,被指派給行程定址空間的實體 RAM 理想上應該要來自本地記憶體。否則每道指令都必須為了程式碼與資料去存取遠端的記憶體。有些僅存於 NUMA 機器的特殊情況要被考慮進去。DSO 的文字區段(text segment)在一台機器的實體 RAM 中通常正好出現一次。但若是 DSO 被所有 CPU 上的行程與執行緒用到(例如,像 libc
這類基本的執行期函式庫),這表示並非一些、而是全部的處理器都必須擁有遠端的位址。作業系統理想上會將這種 DSO「映像(mirror)」到每個處理器的實體 RAM 中,並使用本地的副本。這並非一種最佳化,而是個必要條件,而且通常難以實作。它可能沒有被支援、或者僅以有限的形式支援。
為了避免情況變得更糟,作業系統不該將一個行程或執行緒從一個節點遷移到另一個。作業系統應該已經試著避免在一般的多處理器機器上遷移行程,因為從一個處理器遷移到另一個處理器意味著快取內容遺失。若是負載分配需要將一個行程或執行緒遷出一個處理器,作業系統通常能夠挑選任一個擁有足夠剩餘容量的新處理器。在 NUMA 環境中,新處理器的選擇受到稍微多一些的限制。對於行程正在使用的記憶體,新選擇的處理器不該有比舊的處理器還高的存取成本;這限制目標的清單。若是沒有符合可用標準的空閒處理器,作業系統除了遷移到記憶體存取更為昂貴的處理器以外別無他選。
在這種情況下,有二種可能的方法。首先,可以期盼這種情況是暫時的,而且行程能夠被遷回到一個更合適的處理器上。或者,作業系統也能夠將行程的記憶體遷移到更靠近新使用的處理器的實體分頁上。這是個相當昂貴的操作。可能得要複製大量的記憶體,儘管不必在一個步驟中完成。當發生這種情況的時候,行程必須 –– 至少短暫地 –– 中止,以正確地遷移對舊分頁的修改。有了讓分頁遷移高效又快速,有整整一串其它的必要條件。簡而言之,作業系統應該避免它,除非它是真的有必要的。
一般來說,不能夠假設在一台 NUMA 機器上的所有行程都使用等量的記憶體,使得 –– 由於遍及各個處理器的行程的分佈 –– 記憶體的使用也會被均等地分配。事實上,除非執行在機器上的應用程式非常特殊(在 HPC 世界中很常見,但除此之外則否),不然記憶體的使用是非常不均等的。某些應用程式會使用巨量的記憶體,其餘的幾乎不用。若總是分配產生請求的處理器本地的記憶體,這將會 –– 或早或晚 –– 造成問題。系統最終將會耗盡執行大行程的節點本地的記憶體。
為了應對這些嚴重的問題,記憶體 –– 預設情況下 –– 不會只分配給本地的節點。為了利用所有系統的記憶體,預設策略是條帶化(stripe)記憶體。這保證所有系統記憶體的同等使用。作為一個副作用,有可能變得能在處理器之間自由遷移行程,因為 –– 平均而言 –– 對於所有用到的記憶體的存取成本沒有改變。由於很小的 NUMA 因子,條帶化是可以接受的,但仍不是最好的(見 5.4 節的數據)。
這是個幫助系統避免嚴重問題、並在普通操作下更為能夠預測的劣化(pessimization)。但它降低整體的系統效能,在某些情況下尤為顯著。這即是 Linux 允許每個行程選擇記憶體分配規則的原因。一個行程能夠為它自己以及它的子行程選擇不同的策略。我們將會在第六節介紹能用於此的介面。