今年稍早,我曾撰文介紹我為改善遊戲 VRAM 管理所做的努力。如今,經過數月在郵件列表中的討論,核心補丁(kernel patches)終於合併到主線,並已排入 Linux 7.3 的更新佇列!太棒了!

為了慶祝,讓我們更深入地探討我在上一篇文章中寫的一句話:

「遊戲的表現應該會更穩定——只要遊戲本身使用的 VRAM 沒有超過你實際擁有的容量。」

那麼,有人可能會問:如果遊戲使用的 VRAM 超出了你實際擁有的容量呢?

通常的預期是,一旦發生這種情況,你就大勢已去。遊戲會開始頻繁崩潰,效能驟降至無法遊玩的程度,良好的遊戲體驗變得不可能。

但這真的是不可避免的生活事實嗎?究竟是什麼讓 VRAM 不足如此糟糕?最重要的是:我們如何盡可能減輕它的影響?

理論上,VRAM 不足應該只是一個效能問題,而不是穩定性問題。VRAM 過度承諾(overcommit)的支援由來已久:如果驅動程式過度承諾 VRAM,你通常可以請求任意數量的 VRAM,而核心驅動程式會決定它能放入 GPU 實體記憶體中的多少。

在效能方面,當你 VRAM 不足時效能不佳的根本原因相當簡單。一旦遊戲請求的 VRAM 超出實體記憶體容量,遊戲的部分記憶體就必須被移至 CPU RAM。對 GPU 而言,存取 CPU RAM 比 VRAM 慢得多:不僅 CPU RAM 通常比專用 GPU 的 VRAM 慢,所有記憶體存取都必須經過 PCI 匯流排。PCI 匯流排會增加延遲,並且在從 CPU 記憶體擷取資料時,通常也是頻寬的瓶頸。

由於 PCI 速度的限制,在 VRAM 過度承諾時存在一些真正不可避免的效能瓶頸。假設 GPU 是透過 PCIe 4.0x16 連線,你將獲得略低於 32GiB/s 的頻寬。每毫秒,該 PCIe 匯流排可以傳輸約 32.2MiB 的資料。對於最低 30 影格率(每影格 33.3ms)而言,GPU 能夠存取的資料總量上限約為 1,075.5MiB,略多於 1GiB 的資料。換句話說,如果被逐出的記憶體量大到 GPU 在單一影格中需要從被逐出的記憶體中擷取超過 1GiB 的資料,那麼達到 30 FPS 是不可能的。

同時,僅僅從 CPU 記憶體讀取少量資料對 GPU 效能並非立即的死刑。事實上,即使有充足的 VRAM 可用,GPU 驅動程式有時也會讓指令緩衝區資料和相關配置檔保留在 CPU RAM 中!每當 GPU 執行這些指令時,它都必須存取 CPU 記憶體,但在這些情況下一切都運行得很好。那麼,這些存取有何不同——為何它們沒問題,而 VRAM 不足卻似乎是災難性的?

影響計算的一個重要因素是快取。由於快取命中時的存取延遲與快取記憶體位於 CPU 還是 GPU 無關,因此透過 PCI 匯流排擷取的高初始成本可以透過快取命中來分攤(在一定程度上)。我們可以透過編寫微基準測試來估計存取 CPU RAM 和 VRAM 之間的延遲差異,這些測試會測量不同緩衝區大小的存取延遲(使用對抗性存取模式以盡可能減少快取命中率)。結果可能看起來像這樣(在 RDNA3 上擷取的):

正如預期的那樣,如果緩衝區適合 L2(或任何較高級別的快取),那麼由 CPU RAM 或 VRAM 支援的記憶體的存取延遲是完全相同的,因為資料直接從快取中擷取。在 6MB(RDNA3 上的 L2 快取大小)時,CPU 記憶體延遲會上升到每次存取約 2400 個週期,而裝置記憶體延遲則保持在大致相同的範圍內。請注意,VRAM 存取也會經過 Infinity Cache,但 CPU 記憶體存取則不會(它們在 L2 未命中時直接命中 PCIe)。我懷疑這是因為 Infinity Cache 直接位於 VRAM 之上,因此任何未命中 VRAM 的存取也不會到達 Infinity Cache。

顯然,記憶體一開始並不會被快取,所以第一次存取仍然會有相當高的延遲。此外,失去 Infinity Cache 也確實會造成損害:PCIe 擷取的延遲大約是 Infinity Cache 命中的 7.3 倍,以及是從 VRAM 擷取的 4.6 倍。這種增加的延遲需要非常高的快取命中率才能完全分攤 PCIe 的成本。這意味著只有一小部分使用案例,其中使用 CPU 記憶體會導致如此微小的減慢,以至於在你有選擇的情況下,你會主動選擇使用它而不是 VRAM。當你從 VRAM 中逐出記憶體時,幾乎不可避免地會導致效能有所下降。

儘管如此,即使減慢是不可避免的,但有些記憶體逐出會比其他記憶體對整體效能的影響更大。以快取友善方式存取的記憶體受 CPU RAM 減慢的影響較小。如果存取模式不快取友善,但記憶體不常被存取,情況也可能仍然良好,因為 GPU 很少需要實際從 CPU RAM 擷取資料。可能有許多記憶體配置檔,GPU 只會存取其中一小部分,而從未讀取其餘部分。如果這些配置檔被逐出,你可能會逐出數 GiB 的資料,但實際存取的資料量每影格仍遠低於 1GiB 的硬性限制。

所有這些變數使得預測記憶體被逐出時實際效能如何變得出奇地困難。但總之:根據被逐出記憶體被存取的程度以及這些存取如何快取,你可能能夠在不(完全)損壞效能的情況下耗盡 VRAM!

我們已經理論化了 VRAM 過度承諾的效能問題。太棒了!讓我們啟動 SteamOS,啟動某個遊戲並調高設定——radv/amdgpu:提交指令記憶體不足。

結果發現,實際上 VRAM 不足確實會帶來許多穩定性問題。

這個錯誤與一般的「無法配置,記憶體不足」錯誤並不完全相同。請注意,該訊息特別抱怨指令提交:RADV 在核心嘗試提交指令時返回 -ENOMEM 時列印此訊息,但僅提交指令並不會配置任何新資源!所有指令緩衝區都已預先配置,顯然它們的配置成功了。即使所有記憶體都已成功配置,在 GPU 提交中使用它時突然出現「記憶體不足」錯誤。

是時候進行另一次核心冒險了!當然,讓核心接受提交應該不難——畢竟,核心已經接受了所有配置!

amdgpu 驅動程式在每次提交時必須做的一件事,才能指示 GPU 開始執行指令,就是確保 GPU 指令可能引用的所有記憶體都是可存取的。對於更現代的無綁定圖形 API,你必須假設所有配置的記憶體都可能在某個時候被引用。因此,amdgpu 將嘗試確保所有配置的記憶體也是可存取的。

每個記憶體配置檔都包含有關記憶體類型(在此目的下,系統 RAM 或 GPU VRAM)的資訊,可以從中正確存取。大多數配置檔可以從 CPU RAM 或 VRAM 存取,而 amdgpu 對記憶體配置檔位於這兩種記憶體類型中的任何一種都感到滿意。然而,有些配置檔必須僅放置在 VRAM 中。如果這些記憶體配置檔因其他應用程式在此期間配置了 VRAM 而被逐出到系統 RAM,amdgpu 將必須將它們移回 VRAM。由於沒有任何可用的 VRAM,將配置檔移回需要逐出其他東西。由於某種原因,這失敗了,核心回報了記憶體不足的條件。

為了解釋為何隨機逐出配置檔會失敗,我們必須稍微繞道看看核心如何處理 GPU 配置檔的(CPU 端)鎖定。為了逐出記憶體配置檔,你必須取得與該配置檔關聯的鎖。然而,在提交期間,你還必須鎖定提交中引用的每個配置檔,以防止其他應用程式在你準備 GPU 工作時將該配置檔移至他處。但如果另一個 GPU 提交同時執行相同操作,你可能會遇到以下情況:

如果一個提交想要逐出另一個提交已鎖定的配置檔,但該其他提交也需要鎖定第一個提交的配置檔才能取得進展,我們就遇到了教科書式的 ABBA 死鎖情況。

但別擔心,核心知道如何偵測和解決死鎖!死鎖偵測的詳細資訊已在此核心文件頁面中說明,但總體來說,核心會將鎖定操作與「交易」(基本上只是追蹤已取得哪些鎖)關聯起來。如果兩個交易會死鎖,其中一個交易會被標記為「受傷」(wounded),下次嘗試取得鎖時,會返回 -EDEADLCK 錯誤。此錯誤要求中止交易:應釋放交易期間取得的所有鎖,並從頭開始重新啟動交易。在指令提交的上下文中,這只是意味著驅動程式將重新啟動遍歷所有記憶體配置檔並確保它們可存取的過程。

那麼問題出在哪裡?沒有問題。這種方法非常可靠,並且效果很好。

至少只要它在所有地方都已實施。

在圖形子系統中,受傷-中止-重試迴圈的底層內部透過一個名為 drm_exec 的小型輔助函式庫進行抽象。你不需要手動追蹤哪些配置檔被鎖定,並在遇到 -EDEADLCK 時釋放鎖,你只需使用 drm_exec_lock_obj 輔助函式。如果你研究 TTM(Linux 共用 GPU 記憶體管理層)中的鎖定程式碼,你會注意到它嚴重缺乏使用 drm_exec。

甚至還有一個註解提到 -EDEADLCK 會導致逐出失敗。找到了!一旦在指令提交期間因記憶體壓力過大而遇到此死鎖情況,核心就會退出並拒絕提交,而不是重試。

已經有一些補丁集試圖將 drm_exec 輔助函式連接到 TTM,最早可以追溯到 2024 年,但由於一些尚未解決的錯誤,它們從未被納入。我的工作就是:將補丁集重新基礎化到我的核心版本之上,並找出那些剩餘的錯誤。

重新基礎化補丁集並不太麻煩,找出錯誤只花了一週的時間,期間我忍受了遊戲在 VRAM 爭用嚴重時隨機掛起的痛苦。不算太糟!

我試圖重新發送帶有我發現的所有錯誤修復的補丁集,希望這次能被納入,但在此之前還需要更多工作才能合併。

現在,VRAM 不足至少不會隨機崩潰你的應用程式,我們至少可以適當地調高設定並查看效能。最初的結果給了我一張非常棒的效能圖,如下所示:

找出效能如此糟糕的原因需要弄清楚系統實際上在做什麼這麼慢。對於廣泛的「核心驅動程式在做什麼?」這類問題,我喜歡使用 gpuvis。gpuvis 使用核心追蹤點來建立事件時間線(包括「GPU 工作提交開始/停止」,從中可以推斷每次提交所花費的時間)。

使用追蹤資料啟動 gpuvis,時間線顯示了以下情況:

結果發現,大部分時間實際上並不是花在處理提交上(這是 gfx_0.0.0 活動),而是花在準備提交的記憶體搬移上(sdma0 活動)!

如果使用 gpuvis 的事件列表,並篩選只顯示特定緩衝區物件的捕獲移動事件(我隨機選擇了一個,大多數緩衝區物件都有類似的模式),那麼經常發生緩衝區移動的原因就更明顯了:

列表清楚地顯示,爭用的進程(在此情況下是 gamescope 和遊戲本身)會不斷地輪流逐出和移回相同的記憶體塊,一遍又一遍。這非常糟糕!這讓我想起了我在第一篇部落格文章中寫過的一件事:

一般來說,預期兩個競爭的應用程式會大致輪流執行 GPU 工作——先是一個應用程式提交工作,然後是另一個,然後是第一個,依此類推。透過這種方法,記憶體會在每次提交後不斷地來回移動。一個應用程式被踢出並立即移回,又踢出另一個(在下一步將記憶體移回)。所有這些移動最終導致效能比記憶體從未移動過還要差。

這描述了一個舊問題,即過於積極的 VRAM 分配會導致持續不斷的乒乓式移動。但這個問題自從不再嘗試在沒有剩餘 VRAM 的情況下聲稱 VRAM 後就已修復,而且核心只有在我實施了 dmem cgroup 的 VRAM 保護後才開始變得有些積極。顯然,這一定以某種方式重新引入了乒乓效應。

概念上,dmem cgroup VRAM 保護的設計不應該導致乒乓移動,因為核心只應該逐出沒有關聯的 cgroup VRAM 保護的記憶體。沒有 VRAM 保護,你通常不應該被允許逐出受保護的 VRAM。

這個規則的唯一例外是絕對必須存在於 VRAM 中的記憶體,以便系統正常運作。這些類型的記憶體配置檔始終允許移至 VRAM 以確保系統穩定性。通常,應用程式提供的幾乎沒有東西是正確運作所必需的,但有一個來自應用程式的緩衝區物件是:包含要掃描到顯示器的影像資料的緩衝區。

顯示硬體不僅喜歡掃描輸出的影像位於 VRAM 中,它還完全繞過了 GPU 的虛擬記憶體架構,並且僅使用實體位址。因此,掃描輸出的影像也必須在實體記憶體中是連續的。

透過虛擬記憶體和頁面表格的功能,典型的應用程式緩衝區僅在虛擬記憶體中是連續的,並且可能分散在實體記憶體的所有地方。虛擬位址 0x5000 的緩衝區的第一頁可能在頁面表格中映射到實體位址 0x1234000,但虛擬位址 0x6000 的第二頁可能映射到實體位址 0x4321000,完全不同的地方!

這是一個圖表,用於視覺化虛擬配置檔到實體配置檔的映射,在記憶體碎片化嚴重的情況下(通常在 VRAM 非常少時):

箭頭顯示了第一個配置檔的不同區段到實體記憶體區段的頁面表格映射。為了清晰起見,其他配置檔省略了。

如果你正在配置顯示掃描輸出資料,這種碎片化是不可接受的,因為實體記憶體必須是連續的。這與其他資料的逐出有非常非常不幸的互動。假設掃描輸出資料已被逐出,但現在該資料需要掃描輸出,因此必須將其移回 VRAM。

僅僅逐出一個緩衝區是不夠的,即使該緩衝區的大小與顯示掃描輸出資料相同,因為逐出它並不能產生足夠的連續實體空間來放置掃描輸出資料!更糟糕的是,逐出演算法根本不考慮實體記憶體限制。它是一個非常簡單的迴圈,類似於

使用此演算法(假設配置檔按 LRU 順序排列),即使你逐出前 3 個配置檔(綠色、藍色和紅色),也無法獲得足夠大的空間來容納掃描輸出緩衝區!即使是最大的可用空間也略嫌太小,如這個更新的圖表所示:

在我們的範例中,要找到足夠大的實體連續記憶體區域,VRAM 中的每個配置檔最終都會被逐出!在實際情況中,我觀察到最多 4GiB 的 VRAM 被清除,只是為了給掃描輸出影像騰出空間(對於 R11G11B10 像素格式,每個影像約 32MiB 的像素資料)。這將會非常痛苦!根據前面估計的 PCIe 傳輸速率,僅移動所有這些資料離開 VRAM 就需要至少約 130ms。

雖然掃描輸出無疑是最嚴重的失敗案例,但這個問題更普遍:總會有某些記憶體配置檔會被一次又一次地移入 VRAM,可能會將應用程式可能希望保留在 VRAM 中的某些記憶體擠出。抵抗這種情況並試圖將被逐出的記憶體移回很可能會適得其反。

儘管 dmem cgroup 保護並非此問題的完整解決方案,但它確實大大縮小了問題範圍。透過 cgroup 保護,你可以確定任何隨機應用程式都不會試圖隨意踢出重要的遊戲資源。任何被強制移回 VRAM 的記憶體很可能都有充分的理由留在 VRAM 中。因此,即使有 dmem cgroup 保護,我們也應該小心,不要試圖強行回收被逐出的記憶體。

透過一些迭代測試,我認為我已經找到了一套對遊戲在實際情況下遇到的大多數情況都相當有效的啟發式方法(在被重要系統配置檔逐出時不過於激進,同時在遊戲暫停且 Steam 選單運行時,例如逐出大量遊戲記憶體,並在遊戲恢復時能夠相對快速地回收被逐出的記憶體)。

這些啟發式方法大致如下:

在我看來,這在不過度激進地逐出其他應用程式而導致自損腳的情況,以及在大量記憶體突然被逐出時(例如遊戲暫停且使用者在 Steam 上瀏覽而不是玩遊戲)能夠相對快速地恢復之間取得了可接受的平衡。

有了這些啟發式方法後,讓我們終於可以真正地調高設定了。

我最終選擇了《印第安納瓊斯:古老的圓圈》,因為它很方便地提供了一個串流池大小的設定,你可以調整它來直接修改 VRAM 消耗。

結果,即使設定調高到一個相當荒謬的程度,遊戲請求 9GiB 的 8GiB VRAM(即,有整整 1GiB 的過度承諾遊戲資源存在於 CPU 記憶體中),效能也沒有崩潰到深淵!每影格平均 19.6ms 我仍然認為是完全可玩的。

我甚至可以將設定調高到更荒謬的程度,並將過度承諾的記憶體量加倍,遊戲在這個 8GiB 系統上請求 10GiB VRAM(因此有 2GiB 資源被過度承諾)。此時影格時間變異性會大大增加,尖峰經常超過 33.3ms。總體平均值約為 29.8ms,這不算太糟,但特別是與變異性結合時,這在遊戲玩法中會變得明顯。

雖然這已經是巨大的進步,但我們還沒有完全達到目標。VRAM 過度承諾下的體驗有時仍然可能好壞參半,影格時間可能會因你正在查看遊戲中的哪些物件而明顯變化。

請記住,為了獲得真正良好的逐出效能,被逐出的記憶體如何被 GPU 使用非常重要。目前,這根本沒有被考慮在內!如果我們能夠根據應用程式的存取與 CPU 記憶體配合得如何來做出逐出決策,那麼許多變異性可能會消失。

應用程式記憶體存取模式的複雜之處在於,它們實際上只為應用程式所知。因此,驅動程式實際上無法將它們考慮在內。理想情況下,應該有一個 API,應用程式可以向驅動程式提供關於特定記憶體配置檔有多適合被逐出的提示。

正好有 vkSetDeviceMemoryPriorityEXT 這樣的東西!VK_EXT_pageable_device_local_memory 擴充功能提供了我們在這裡需要的一切,它允許應用程式為任何它們想要的裝置記憶體傳達任何它們想要的優先級。只要應用程式透過此擴充功能提供合理的提示,在核心中實施優先級然後利用應用程式提供的優先級就有潛力大大穩定情況!

將優先級連接到核心比你預期的要容易得多。核心已經維護了一個記憶體配置檔的最近最少使用(LRU)列表,在逐出時,會按順序遍歷該列表。對於 LRU 列表中的每個條目,會嘗試逐出,直到有足夠的可用空間為止。

這個 LRU 列表為應該首先逐出哪個應用程式的記憶體提供了一個很好的啟發式方法。長時間沒有提交任何內容的應用程式不太可能很快需要記憶體,而且由於它們的記憶體是最近最少使用的,它會出現在 LRU 列表的早期並首先被逐出。

當一個應用程式使用一組緩衝區時,該緩衝區集會一次性移動到 LRU 列表的末尾。然而,該批次內配置檔的順序並沒有被明確控制。這意味著一旦核心接近要逐出某個應用程式的記憶體時,具體逐出哪些記憶體塊就或多或少是未定義的。簡化的視覺化可能如下所示:

如果核心像這樣遍歷 LRU 列表,它會首先逐出優先級值為 2 的緩衝區,即使 LRU 列表中還有其他優先級較低的緩衝區。如果只逐出優先級為 2 的第一個緩衝區,情況可能還可以,但如果優先級為 4 的重要緩衝區也被逐出,很可能會出現問題。

鑑於我們已經知道個別配置檔的具體優先級,這個 LRU 列表是一個整合它們的非常簡單的地方。就像按優先級對單個應用程式內的列表條目進行排序一樣簡單:

現在,當核心遍歷 LRU 列表以尋找要逐出的內容時,它找到並嘗試逐出的第一件事將是最低優先級的緩衝區。最高優先級的緩衝區位於列表的末尾,因此只有在逐出所有較低優先級緩衝區後仍然不足時才會被逐出。

不幸的是,並非所有應用程式都透過 VK_EXT_pageable_device_local_memory 設定優先級。對於原生 Vulkan 應用程式,我至少沒有觀察到任何 idTech 遊戲直接使用該擴充功能。

D3D 端看起來好多了,因為 vkd3d-proton 在可用時使用 VK_EXT_pageable_device_local_memory,並將 ID3D12Device::MakeResident / ID3D12Device::Evict API 調用以及透過 ID3D12Device1::SetResidencyPriority 設定的優先級轉換為使用 Vulkan vkSetDeviceMemoryPriority 命令設定的優先級值。許多 D3D12 遊戲至少利用了其中一項 API,因此這些遊戲提供的提示現在將被利用。

我沒有非常確切的數據說明大多數 D3D12 應用程式究竟過度承諾了多少記憶體,因為它們通常不像 idTech 的效能疊加層那樣以易於存取的方式暴露它們請求的 VRAM 總量。然而,正確處理記憶體優先級通常似乎有很大的機會改善體驗。效能通常隨時間看起來更穩定(因為你不再依賴核心逐出哪些緩衝區的運氣)。在某些地方,我有一個好的比較點,我懷疑在最好的情況下,與核心隨機逐出東西相比,效能提高了高達 30%——但再次強調,這個數字幾乎完全取決於逐出的運氣。

總而言之,VRAM 不足的表現如何?

我認為還算不錯!在許多情況下,你可能會驚訝於即使逐出了一 GB 或更多的記憶體,你仍然能保留多少效能!話雖如此,這當然是一個相當樂觀的情況,錯誤的東西進入 CPU RAM 會很快導致非常顯著的減慢。逐出很難做到恰到好處,而且在一定程度上,效能總會受到影響。如果一個遊戲即使在所有東西都在 VRAM 中時也難以達到 30fps,那麼在此之上還需要逐出某些東西,有時可能會不可避免地導致錯失 30fps 的目標。

儘管如此,我希望這篇部落格文章能夠證明,即使你最終有一些記憶體被逐出到系統 RAM,減慢也是可以管理的。驅動程式(特別是核心驅動程式)可以採取措施來盡可能快地實現過度承諾,甚至應用程式也可以透過與驅動程式堆疊協調來減輕其記憶體被逐出的影響。一切就緒後,VRAM 過度承諾實際上並不像人們最初認為的那樣是個大問題。

我這裡描述的所有工作都已經在 SteamOS 中發布了一段時間了(它同時存在於 Stable 和 Preview 版本中。只要你的系統是最新的,就可以使用了!)。

當然,我已經在努力將所有這些工作上游化,以便每個人都能使用!然而,這裡涉及許多活動部件和對一些核心概念的深度重構,所以可能需要時間才能全部合併到主線上。

同時,我也不想寫一篇部落格文章,談論很多很酷的程式碼,最後卻說「實際上你無法自己看到,請等到它全部上游 lol」。

作為折衷,我已將核心工作重新基礎化到最近的主線核心版本上,並在此處發布了一個 git 分支。雖然理論上應該會產生類似的效果,但它沒有經過 SteamOS 核心那樣嚴格的測試。可能會有 SteamOS 版本中不存在的錯誤和不穩定。基本上,風險自負。我不期望能大規模維護這個分支,因為我更願意專注於將補丁正確地合併到主線上。

為了將應用程式優先級提示傳遞給核心,你還需要一個我在此處推送的自訂 Mesa 分支。與核心分支類似的考量也適用於此。

雖然我認為我對記憶體管理驅動程式端有相當好的了解,但我對應用程式如何在內部決定提供記憶體管理啟發式方法完全不熟悉。我懷疑優化你已經耗盡 VRAM 的情況並不是開發人員待辦事項清單上的首要任務(誰知道呢,也許記憶體稀缺正在改變這一點?:P),所以也許在那裡還有一些未探索的效能改進空間?

如果你,親愛的讀者,碰巧了解大型遊戲/引擎的 VRAM 管理(尤其是在耗盡時),我很樂意與你交流!我有一種預感,透過讓應用程式和驅動程式更好地協調,仍然可以獲得效能提升,但我也對從應用程式開發者的角度來看事情如何感興趣。

其中一個原因是,指令緩衝區特別小,這對於被逐出的 VRAM 來說並不是一個考慮因素(你對要逐出多少沒有選擇)。但這不是唯一的原因:快取/存取模式的考量同樣適用於指令緩衝區和被逐出的 VRAM。↩

即使所有配置都成功了,但如果耗盡了 CPU RAM 和 VRAM,實際上也不可能使用該記憶體進行提交。這是正常的,並且會導致列印相同的錯誤,但這裡發生的不是——在我的情況下,確實有足夠的記憶體可用。↩

嚴格來說,顯示硬體可以處理從系統 RAM 進行掃描輸出!但是,在 VRAM 和系統 RAM 之間移動會帶來一些糟糕的權衡,所以為了簡單起見,我們忽略這一點。↩

如果緩衝區在實體上是連續的,對效能更好,但連續性並非嚴格要求。↩

實際上,最先配置的緩衝區很可能是最先被逐出的緩衝區之一,而最近配置的緩衝區則是最後。↩

排序僅對於單個應用程式的優先級值才真正可行,因為優先級僅在同一上下文中的其他優先級相關時才有意義。不同的應用程式很可能對某些特定絕對優先級值意味著什麼有不同的解釋/尺度。↩