基於DAMON的回收

基於DAMON的回收 (DAMON_RECLAIM) 是一個靜態核心模組,旨在輕度記憶體壓力下用於主動且輕量級的回收。它並非旨在取代基於LRU列表的頁粒度回收,而是根據不同的記憶體壓力水平和需求進行選擇性使用。

何時需要主動回收?

在通常的記憶體超配系統上,主動回收冷頁有助於節省記憶體並減少因程序直接回收或kswapd的CPU消耗引起的延遲峰值,同時只產生極小的效能下降[1] [2]

空閒頁報告[3] 基於記憶體超配的虛擬化系統是此類情況的一個很好的例子。在此類系統中,訪客虛擬機器向宿主機報告其空閒記憶體,宿主機將報告的記憶體重新分配給其他訪客。結果,系統記憶體得到充分利用。然而,訪客可能並不那麼節省記憶體,這主要是因為某些核心子系統和使用者空間應用程式被設計為儘可能多地使用可用記憶體。因此,訪客可能只向宿主機報告少量空閒記憶體,導致系統記憶體利用率下降。在訪客中執行主動回收可以緩解這個問題。

工作原理?

DAMON_RECLAIM 查詢在特定時間段內未被訪問的記憶體區域並將其換出。為避免換出操作消耗過多CPU,可以配置一個速度限制。在速度限制下,它會優先換出長時間未被訪問的記憶體區域。系統管理員還可以配置在何種情況下,該方案應根據三個記憶體壓力水位線自動啟用和停用。

介面:模組引數

要使用此功能,您首先應確保您的系統正在執行一個使用CONFIG_DAMON_RECLAIM=y構建的核心。

為了讓系統管理員能夠啟用或停用它並針對給定系統進行調整,DAMON_RECLAIM 利用了模組引數。也就是說,您可以在核心啟動命令列上新增damon_reclaim.<parameter>=<value>,或者將適當的值寫入/sys/module/damon_reclaim/parameters/<parameter>檔案。

以下是每個引數的描述。

enabled (啟用)

啟用或停用 DAMON_RECLAIM。

您可以透過將此引數的值設定為Y來啟用 DAMON_RECLAIM。將其設定為N將停用 DAMON_RECLAIM。請注意,由於基於水位線的啟用條件,DAMON_RECLAIM 可能無法進行實際的監控和回收。有關此內容的詳情,請參閱下方水位線引數的描述。

commit_inputs (提交輸入)

使 DAMON_RECLAIM 再次讀取輸入引數,但除了enabled之外。

DAMON_RECLAIM 執行時更新的輸入引數預設不生效。一旦此引數設定為Y,DAMON_RECLAIM 將再次讀取除enabled之外的所有引數值。重新讀取完成後,此引數將設定為N。如果在重新讀取時發現無效引數,DAMON_RECLAIM 將被停用。

min_age (最小生存期)

用於識別冷記憶體區域的時間閾值,以微秒為單位。

如果一個記憶體區域在此時間或更長時間內未被訪問,DAMON_RECLAIM 會將該區域識別為冷區域並進行回收。

預設為 120 秒。

quota_ms (時間配額)

回收操作的時間限制,以毫秒為單位。

DAMON_RECLAIM 嘗試在一個時間視窗 (quota_reset_interval_ms) 內,最多使用此時間來嘗試回收冷頁。這可以用於限制 DAMON_RECLAIM 的 CPU 消耗。如果值為零,則此限制被停用。

預設為 10 毫秒。

quota_sz (大小配額)

回收操作的記憶體大小限制,以位元組為單位。

DAMON_RECLAIM 會在一個時間視窗 (quota_reset_interval_ms) 內統計其嘗試回收的記憶體量,並確保不超過此限制。這可以用於限制 CPU 和 IO 的消耗。如果此值為零,則此限制被停用。

預設為 128 MiB。

quota_reset_interval_ms (配額重置間隔)

時間/大小配額的費用重置間隔,以毫秒為單位。

也就是說,DAMON_RECLAIM 在 quota_reset_interval_ms 毫秒內,不會嘗試回收超過 quota_ms 毫秒的時間或 quota_sz 位元組的記憶體。

預設為 1 秒。

quota_mem_pressure_us (記憶體壓力微秒配額)

記憶體壓力停頓時間的期望級別,以微秒為單位。

在保持其他配額設定的上限的同時,DAMON_RECLAIM 自動增減配額的有效級別,以期達到此記憶體壓力水平。系統範圍的some記憶體PSI(每配額重置間隔quota_reset_interval_ms內的微秒數)會被收集並與此值進行比較,以檢視是否滿足目標。值為零表示停用此自動調整功能。

預設停用。

quota_autotune_feedback (配額自動調整反饋)

用於自動調整有效配額的使用者可指定反饋。

在保持其他配額設定的上限的同時,DAMON_RECLAIM 自動增減配額的有效級別,以期從使用者那裡收到值為10,000的反饋。DAMON_RECLAIM 假定反饋值和配額成正比。值為零表示停用此自動調整功能。

預設停用。

wmarks_interval (水位線檢查間隔)

當 DAMON_RECLAIM 已啟用但因其水位線規則而處於非活動狀態時,檢查水位線前的最小等待時間。

wmarks_high (高水位線)

高水位線的空閒記憶體率(每千分比)。

如果系統每千位元組的空閒記憶體量高於此值,DAMON_RECLAIM 將變為非活動狀態,因此它不執行任何操作,只定期檢查水位線。

wmarks_mid (中水位線)

中水位線的空閒記憶體率(每千分比)。

如果系統每千位元組的空閒記憶體量介於此值和低水位線之間,DAMON_RECLAIM 將變為活動狀態,從而開始監控和回收。

wmarks_low (低水位線)

低水位線的空閒記憶體率(每千分比)。

如果系統每千位元組的空閒記憶體量低於此值,DAMON_RECLAIM 將變為非活動狀態,因此它不執行任何操作,只定期檢查水位線。在這種情況下,系統將回退到基於LRU列表的頁粒度回收邏輯。

sample_interval (取樣間隔)

監控的取樣間隔,以微秒為單位。

DAMON 用於冷記憶體監控的取樣間隔。詳情請參閱 DAMON 文件 (詳細用法)。

aggr_interval (聚合間隔)

監控的聚合間隔,以微秒為單位。

DAMON 用於冷記憶體監控的聚合間隔。詳情請參閱 DAMON 文件 (詳細用法)。

min_nr_regions (最小監控區域數)

最小監控區域數。

DAMON 用於冷記憶體監控的最小監控區域數。這可以用於設定監控質量的下限。但是,將其設定得太高可能會導致監控開銷增加。詳情請參閱 DAMON 文件 (詳細用法)。

max_nr_regions (最大監控區域數)

最大監控區域數。

DAMON 用於冷記憶體監控的最大監控區域數。這可以用於設定監控開銷的上限。然而,將其設定得太低可能會導致監控質量不佳。詳情請參閱 DAMON 文件 (詳細用法)。

monitor_region_start (目標記憶體區域起始物理地址)

目標記憶體區域的起始物理地址。

DAMON_RECLAIM 將對其進行操作的記憶體區域的起始物理地址。也就是說,DAMON_RECLAIM 將在此區域中查詢冷記憶體區域並進行回收。預設情況下,最大的系統RAM用作該區域。

monitor_region_end (目標記憶體區域結束物理地址)

目標記憶體區域的結束物理地址。

DAMON_RECLAIM 將對其進行操作的記憶體區域的結束物理地址。也就是說,DAMON_RECLAIM 將在此區域中查詢冷記憶體區域並進行回收。預設情況下,最大的系統RAM用作該區域。

skip_anon (跳過匿名頁)

跳過匿名頁回收。

如果此引數設定為Y,DAMON_RECLAIM 不會回收匿名頁。預設情況下為N

kdamond_pid (DAMON執行緒PID)

DAMON 執行緒的 PID。

如果 DAMON_RECLAIM 已啟用,此引數將變為工作執行緒的 PID。否則,為 -1。

nr_reclaim_tried_regions (嘗試回收的記憶體區域數)

DAMON_RECLAIM 嘗試回收的記憶體區域數量。

bytes_reclaim_tried_regions (嘗試回收的記憶體區域總位元組數)

DAMON_RECLAIM 嘗試回收的記憶體區域總位元組數。

nr_reclaimed_regions (成功回收的記憶體區域數)

DAMON_RECLAIM 成功回收的記憶體區域數量。

bytes_reclaimed_regions (成功回收的記憶體區域總位元組數)

DAMON_RECLAIM 成功回收的記憶體區域總位元組數。

nr_quota_exceeds (配額超出次數)

時間/空間配額限制被超出的次數。

示例

以下執行時示例命令使 DAMON_RECLAIM 查詢 30 秒或更長時間未訪問的記憶體區域並將其換出。回收操作限制為每秒最多 1 GiB,以避免 DAMON_RECLAIM 在換出操作中消耗過多 CPU 時間。它還要求 DAMON_RECLAIM 在系統空閒記憶體率超過 50% 時不執行任何操作,但當其低於 40% 時開始實際工作。如果 DAMON_RECLAIM 未能取得進展,導致空閒記憶體率低於 20%,它會再次要求 DAMON_RECLAIM 不執行任何操作,以便我們可以回退到基於 LRU 列表的頁粒度回收機制。

# cd /sys/module/damon_reclaim/parameters
# echo 30000000 > min_age
# echo $((1 * 1024 * 1024 * 1024)) > quota_sz
# echo 1000 > quota_reset_interval_ms
# echo 500 > wmarks_high
# echo 400 > wmarks_mid
# echo 200 > wmarks_low
# echo Y > enabled