nbd-vram:把 NVIDIA 显存当交换空间用
一个开源工具 nbd-vram,让你在 Linux 上把 NVIDIA GPU 的显存用作交换空间。
专为混合图形笔记本设计——这类机器的内存焊死在主板上,无法升级。显示走集成 AMD/ATI 显卡,NVIDIA 卡大部分时间闲置,其显存完全未被利用。这个工具就把那些显存拿来当高优先级交换空间用。
实测环境:AMD/ATI + RTX 3070 Laptop(GA104M,16GB RAM,8GB VRAM),驱动 580.159.03,内核 6.17,Pop!_OS。分配了 7GB 作为交换。最终加上 zram 和 SSD 交换,总计约 46GB 可寻址内存,是原机的三倍。溢出顺序:RAM 满 → VRAM 承接(PCIe)→ zram 压缩(CPU)→ SSD 兜底。
工作原理
一个小型守护进程通过 CUDA 驱动 API 分配显存,然后通过 NBD(Network Block Device)协议经由 Unix socket 将其暴露为块设备。内核内建的 nbd 驱动连接到它,暴露为 /dev/nbdX。之后就是普通的交换设备。
数据路径:内核交换子系统 → /dev/nbdX → nbd 内核驱动 → Unix socket → nbd-vram 守护进程 → cuMemcpyHtoD/DtoH → GPU 显存。
不需要编写或维护内核模块。不需要 NVIDIA 内核符号。在内核和驱动更新后无需重新编译任何东西。
为什么不走 NVIDIA P2P API?
"显而易见"的做法是 nvidia_p2p_get_pages_persistent,它将显存页固定在 BAR1 中,使 CPU 可以通过 ioremap_wc 直接访问。但所有走这条路线的现有项目都撞上了同一堵墙:NVIDIA 驱动在消费级 GeForce GPU 上返回 EINVAL。持久和非持久变体都试过,两个 flag 值都试过——这是 NVIDIA RM 层为 Quadro/数据中心 SKU 做的限制,与驱动版本无关。
另一种做法——直接用 ioremap_wc 访问 BAR1 物理地址,不走 P2P API——也行不通。GPU 的内部页表只有约 16 MiB 的 BAR1 被映射(仅显示帧缓冲)。其他地方读回来全是零。
NBD 方法绕过了这一切。cuMemcpyHtoD 和 cuMemcpyDtoH 在任何支持 CUDA 的 GPU 上都能正常工作,无需特殊权限。
安装
git clone https://github.com/c0dejedi/nbd-vram
cd nbd-vram
sudo ./install.sh
sudo systemctl start vram-swap-nbd
验证:
swapon --show
# NAME TYPE SIZE USED PRIO
# /dev/nbd0 partition 7G 0B 1500
性能
测试平台:RTX 3070 Laptop(8GB VRAM),内核 6.17,Pop!_OS。对照 NVMe cryptswap(dm-crypt,PCIe 4.0)。
顺序吞吐量(dd,2GiB)
VRAM 在大块顺序传输上较慢。瓶颈是 NBD+CUDA 的用户态往返开销。NVMe 写 2.7 GB/s 读 2.9 GB/s,VRAM 写 1.1 GB/s 读 2.3 GB/s。
4K 随机 IOPS(fio,libaio,iodepth=32)
NVMe 45k IOPS 平均 343us,VRAM 28k IOPS 平均 550us。NVMe 在持续随机 I/O 上更快。
单次操作延迟(ioping,4K 读取,1 请求/秒)
这才是关键指标。VRAM 平均 335us,NVMe 平均 9.05ms——VRAM 快 27 倍。NVMe 在请求间隔中被 APST(自动电源状态转换)休眠掉,每次都有约 9ms 的唤醒惩罚。而显存没有电源状态,响应时间持续在 133-490us 之间。
笔记本的内存压力很少是持续的 GB/s 洪流——而是每隔几秒到来的单次 4K 缺页中断。9ms 一次的 NVMe 交换你能感觉到,335us 一次的 VRAM 交换你感觉不到。
结语
nbd-vram 为内存焊死的笔记本提供了一条实用的"扩容"路径。不完美(顺序吞吐不如 NVMe),但在实际的零星交换场景下,延迟优势巨大。