Linux下将Nvidia显卡显存用作交换空间
速览
该技巧允许在Linux系统中将Nvidia GPU的VRAM配置为swap交换空间。当系统物理内存不足时,可利用闲置的显存来暂存数据,从而避免OOM错误。这对于显存充裕但内存受限的AI开发或计算环境具有实用价值。
AI 深度解读
在 Linux 上使用 NVIDIA GPU 的显存作为 Swap 空间
背景
在笔记本电脑领域,尤其是那些采用板载内存(soldered memory)且无法升级内存的机型中,物理内存(RAM)的容量往往成为性能瓶颈。当系统内存耗尽时,Linux 内核通常会将数据交换(swap)到 SSD 上。然而,SSD 的读写速度远低于内存,频繁的交换操作会导致严重的性能下降和延迟。
与此同时,许多配备独立显卡(如 NVIDIA RTX 系列)的笔记本电脑拥有大量的视频随机存取存储器(VRAM)。例如,一张 RTX 3070 笔记本显卡可能拥有 8GB 甚至更多的 VRAM。在大多数日常使用场景中,这部分显存并未被完全利用。
为了解决这一资源错配问题,开发者 Sean Lobjoit(GitHub ID: c0dejedi)发布了一个名为 nbd-vram 的工具。该工具允许 Linux 用户将 NVIDIA GPU 的 VRAM 作为交换空间(swap space)使用,从而显著增加系统可寻址的内存总量,缓解物理内存不足带来的压力。
核心内容
nbd-vram 的核心设计理念是绕过 NVIDIA 驱动中对消费者级 GPU 的直接内存访问限制,通过一种无需编写内核模块的架构,将 GPU 显存转化为标准的块设备。
技术架构与数据路径
该方案由一个小型守护进程(daemon)驱动,其工作流程如下:
- 显存分配:守护进程通过 CUDA 驱动 API 分配 VRAM。
- 块设备暴露:利用 NBD(Network Block Device,网络块设备)协议,通过 Unix 套接字(Unix socket)将分配的显存暴露为块设备。
- 内核连接:Linux 内核中内置的
nbd驱动程序连接到该套接字,并在/dev/nbdX处暴露设备。 - 交换功能:此时,
/dev/nbdX就像一个普通的交换设备一样,可以被内核的交换子系统使用。
数据路径详解:
内核交换子系统 -> /dev/nbdX -> NBD 内核驱动 -> Unix 套接字 -> nbd-vram 守护进程 -> cuMemcpyHtoD/cuMemcpyDtoH(CUDA 内存拷贝) -> GPU VRAM。
为什么选择 NBD 方案?
在实现这一功能时,开发者尝试了两种“显而易见”的方法,但均因 NVIDIA 驱动的限制而失败:
-
使用
nvidia_p2p_get_pages_persistent: 这种方法旨在将 VRAM 页面固定在 BAR1 区域,以便 CPU 可以通过ioremap_wc直接访问。然而,现有的所有尝试都撞上了同一堵墙:在消费级 GeForce GPU 上,NVIDIA 驱动会返回EINVAL错误。无论是持久化还是非持久化变体,无论标志位如何设置,该功能仅在 Quadro 或数据中心 SKU 级别受支持,与驱动版本无关。 -
直接映射 BAR1 物理地址: 绕过 P2P API,直接对 BAR1 物理地址进行
ioremap_wc映射。这也行不通,因为 GPU 的内部页表仅映射了约 16 MiB 的 BAR1 空间(仅用于显示帧缓冲区)。对其余部分的读取返回零。虽然mkswap看似成功,但swapon会失败,因为交换头实际上并不存在。
NBD 方案的优势:
nbd-vram 巧妙地避开了上述所有限制。cuMemcpyHtoD 和 cuMemcpyDtoH 函数在任何支持 CUDA 的 GPU 上均可正常工作,无需特殊权限。此外,该方案无需编写或维护内核模块,不涉及 NVIDIA 内核符号,因此在内核和驱动更新后无需重新编译,具有极高的兼容性。
测试环境与性能表现
开发者在以下环境中进行了测试:
- 硬件:RTX 3070 Laptop (GA104M),物理内存 16 GB,VRAM 8 GB。
- 软件:驱动版本 580.159.03,内核 6.17,Pop!_OS。
- 配置:分配 7 GB 用于交换。
内存溢出顺序(Overflow Order): 系统采用分层溢出策略以优化性能:
- RAM 填满:首先使用物理内存。
- VRAM 吸收溢出:当 RAM 不足时,数据溢出到 VRAM。由于通过 PCIe 传输,速度较快。
- zram 压缩:如果 VRAM 也满了,剩余数据由 CPU 进行压缩并存入 zram。
- SSD 交换:仅当上述所有资源耗尽时,才使用 SSD 交换。
性能数据:
在 RTX 3070 Laptop 上通过 test-fill.sh 进行的测试显示(7 GiB 顺序写入,4M 块):
- 顺序吞吐量:约 1.3 GB/s。
- 延迟:由于数据路径是通过 PCIe 到 GPU 而非存储设备,其延迟甚至低于 NVMe SSD。
最终结果(包括 zram 和 SSD 交换)使可寻址内存达到约 46 GB,是原始物理内存的三倍。
安装与配置
系统要求:
- 支持 CUDA 的 NVIDIA GPU(任何消费级 RTX/GTX 卡)。
- 安装了
libcuda.so.1的 NVIDIA 驱动(无需 CUDA Toolkit)。 - Linux 内核 3.0+(大多数发行版已内置
nbd模块)。 - 软件包:
nbd-client,gcc,make。
安装步骤:
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
配置调整:
- 显存大小:编辑
/etc/systemd/system/vram-swap-nbd.service,修改VRAM_SETUP_SIZE_MB(例如 7168 表示 7 GB)。守护进程会尝试分配请求的大小,如果显存不足,会以 512 MiB 为步长回退,因此即使显示合成器已加载,它也能尽可能多地占用显存。 - 优先级:修改
VRAM_SWAP_PRIORITY(数值越高,越优先使用)。建议将 VRAM 交换优先级设置得高于 SSD 交换,以便在 zram 之前吸收溢出。 - 电源管理:安装时可选择启用电源感知管理。如果启用,当拔掉交流电源或电池低于阈值时,服务会自动停止;恢复供电后自动重启。
测试脚本:
sudo bash test-nbd.sh:分配显存,连接 NBD 设备,执行 1 MiB 读写检查,激活交换,并打印卸载说明。sudo bash test-fill.sh:在冒烟测试通过后,用零填充整个 VRAM 分区,验证样本读取,退出后自动恢复交换。
关键要点
- 解决内存瓶颈:专为板载内存且无法升级的笔记本电脑设计,有效利用闲置的 GPU 显存作为交换空间。
- 绕过驱动限制:通过 NBD 协议和 CUDA 内存拷贝 API,规避了 NVIDIA 驱动在消费级 GPU 上禁止直接物理内存访问(P2P)的限制。
- 零内核模块依赖:无需编写或维护内核模块,不依赖 NVIDIA 内核符号,兼容性强,随内核/驱动更新无需重新编译。
- 高性能溢出层:VRAM 交换速度(~1.3 GB/s)快于 SSD,且延迟更低,作为 RAM
