← 返回 Siami 首頁

把 Nvidia 顯示卡 VRAM 當 Linux swap 空間用:nbd-vram 開源專案解析

▲ 183 💬 50
把 Nvidia 顯示卡 VRAM 當 Linux swap 空間用:nbd-vram 開源專案解析

如果你的筆電記憶體是焊死在主機板上、無法升級,但顯示卡上還閒置著 8GB VRAM,這個開源專案可以讓你把 GPU 的 VRAM 當成 Linux 的 swap 空間來用。專案作者 Sean Lobjoit(c0dejedi)把這個工具命名為 nbd-vram,整套機制靠 CUDA 驅動 API 與 Linux 內建的 NBD(Network Block Device)協定串起來,不需要寫 kernel module,也不需要 NVIDIA 私有 kernel 符號。

實測環境:RTX 3070 筆電版(GA104M、實體 16GB 記憶體、VRAM 8GB)、驅動 580.159.03、kernel 6.17、Pop!_OS。配置 7GB VRAM 當 swap,加上原本的 zram 與 SSD swap,整體可定址記憶體達到約 46GB,是原本 RAM 量的三倍。溢位順序設計成:先把 RAM 填滿 → VRAM 透過 PCIe 快速吸收溢位 → zram 用 CPU 壓縮剩下的資料 → SSD swap 只在所有空間都耗盡時才動用。

運作原理

整個架構由一個小型 daemon 組成。它先用 CUDA 驅動 API 從 VRAM 分配一塊空間,再用 NBD 協定透過 Unix socket 把它掛出成區塊裝置。Linux 內建的 nbd 驅動會自動連上 socket、產出 /dev/nbdX,從此它就是一個普通的 swap 裝置。

資料路徑很直觀:kernel swap 子系統 → /dev/nbdX → nbd kernel 驅動 → Unix socket → nbd-vram daemon → cuMemcpyHtoD / cuMemcpyDtoH → GPU VRAM。

這種設計的優點是:不需要寫 kernel module、不依賴 NVIDIA kernel 符號,因此即使 kernel 與驅動更新,daemon 也不用重編。

為什麼不走 NVIDIA P2P API?

直覺的做法是用 nvidia_p2p_get_pages_persistent 把 VRAM page pin 在 BAR1,再用 ioremap_wc 讓 CPU 直接存取。但所有走這條路的現有專案都撞到同一面牆:NVIDIA 驅動在消費級 GeForce 卡上會回傳 EINVAL,無論是 persistent 還是非 persistent 變體、哪種 flag 組合都一樣。這個限制寫在 RM(Resource Manager)層,只對 Quadro 與資料中心 SKU 開放,跟驅動版本無關。

另一個變通方法是跳過 P2P API、直接 ioremap_wc BAR1 實體位址,但這條路也不通。GPU 內部 page table 只映射大約 16 MiB 的 BAR1(剛好等於顯示 framebuffer),其他位址讀回來都是 0。表面上 mkswap 會成功,但 swapon 會失敗,因為 swap header 根本不在那裡。

nbd-vram 走 NBD 這條路就完全繞過這些限制:cuMemcpyHtoDcuMemcpyDtoH 在任何 CUDA GPU 上都能用、不需要任何特殊權限。

系統需求

  • 支援 CUDA 的 NVIDIA GPU(任何消費級 RTX/GTX 卡都行)
  • 內含 libcuda.so.1 的 NVIDIA 驅動(不需要完整 CUDA toolkit)
  • Linux kernel 3.0 以上(內建 nbd 模組,大部分發行版都有)
  • nbd-client 套件
  • gccmake

安裝步驟

git clone https://github.com/c0dejedi/nbd-vram
cd nbd-vram
sudo ./install.sh
sudo systemctl start vram-swap-nbd

驗證 VRAM swap 是否掛載成功:

swapon --show
# NAME       TYPE      SIZE USED PRIO
# /dev/nbd0  partition   7G   0B 1500

安裝時 service 預設是 enabled,每次開機都會自動啟動。

設定參數

編輯 /etc/systemd/system/vram-swap-nbd.service

Environment=VRAM_SETUP_SIZE_MB=7168    # 要分配多少 VRAM
Environment=VRAM_SWAP_PRIORITY=1500   # swap 優先權(數字越高越先用)

daemon 會先嘗試設定的大小,如果 GPU 被顯示占用太多就以 512 MiB 為單位往下退讓,所以 VRAM_SETUP_SIZE_MB 是上限而不是硬性需求,會盡量抓滿能用的空間。

改完之後執行:

sudo systemctl daemon-reload && sudo systemctl restart vram-swap-nbd

電源管理

安裝程式第一次安裝時會詢問是否啟用電源感知管理。啟用後,service 會在拔掉 AC 電源(或電量低於門檻)時自動停止,接上電源後自動重啟。手動 systemctl stop 永遠會被尊重、不會被覆蓋。

安裝後要改設定,編輯 /etc/nbd-vram.conf,新設定會在下次輪詢(60 秒內)生效,或者下次插拔 AC 電源時立即生效。

冒煙測試(不安裝也能跑)

sudo bash test-nbd.sh

會依序配置 VRAM、連上 NBD 裝置、做 1 MiB 寫入/讀回檢查、啟用 swap,最後印出拆卸步驟。如果當前有測試實例在跑,install.sh 會自動處理拆卸。

冒煙測試通過後要對整個 partition 做壓力測試:

sudo bash test-fill.sh

會把整個 VRAM partition 寫滿 0、抽樣讀回驗證,結束時自動還原 swap。

效能數據

在 RTX 3070 筆電版上用 test-fill.sh 實測(7 GiB 循序寫入、4M block):

  • 循序吞吐量:約 1.3 GB/s
  • 延遲比 NVMe 還低,因為資料路徑是 PCIe 到 GPU,而不是走儲存裝置

如果你的筆電已經有 zram,建議把 VRAM swap 設成較高優先權,這樣溢位會先被 VRAM 吸收,最後才落到 SSD。

解除安裝

sudo bash uninstall.sh

授權

MIT - Sean Lobjoit(c0dejedi)

Reddit 熱門留言 (4)

#1 Hacker News ▲ 95
Gen 4.0 x16 是 32 GB/s 雙向頻寬,但這個實作方式根本不是追求高效能的做法。編按:他們的 benchmark 跑在 ZRAM 之上,ZRAM 會先壓縮 page 再寫到 swap,額外負擔不小。首先這是個 userspace 程式掛在 nbd 驅動上,nbd 驅動本來就知道很慢。資料在送到 GPU 之前還會在 userspace 繞一圈 bounce buffer,所以 kernel 寫入 NBD 裝置時:先複製到 userspace → 再 CUDA 複製到 GPU。本來就已經比 SSD swap 慢,所以把它放在比 SSD 高的優先權其實是錯的。
#2 Hacker News ▲ 67
擴充卡上的記憶體不是被 PCIe 5 限制,是被 CXL 支援限制。CXL 跟 PCIe 共用電氣/物理層,但協定完全不同。把系統記憶體放上 PCIe 卡的問題是 PCIe 不是快取一致性的互連。如果有一個 cache line 在 GPU 上、位於處理器的 cache 內,GPU、其他 CPU 核心或任何其他 PCIe 裝置對該記憶體的遠端修改都不會失效 CPU 的 cache line。這就是為什麼這件事需要 CXL。
#3 Hacker News ▲ 42
Swap 應該被視為回收平等性的機制,而不是緊急的額外記憶體。回收平等性是指檔案支援的 page 與匿名 page 在被驅逐出實體記憶體時適用相似標準。我以前的 Linux 桌面完全不設 swap,但這篇說服我至少加一個小的 swap partition。
#4 Hacker News ▲ 28
我曾認真考慮過用這方法來改善一台 QNAP 2U 機器的 RAM 狀況(我找不到相容 RAM)。合法的 memory-over-PCIE 被 PCIe 5 跟晶片組支援綁死真的有點煩。這個實作跑太多層了,效能不會太好。為什麼不寫一個自訂的 block driver?