---
title: "리눅스에서 놀고 있는 엔비디아 VRAM을 스왑 공간으로 쓰는 오픈소스 프로젝트"
published: 2026-06-02T22:55:33.000Z
canonical: https://jeff.news/article/3652
---
# 리눅스에서 놀고 있는 엔비디아 VRAM을 스왑 공간으로 쓰는 오픈소스 프로젝트

하이브리드 그래픽 노트북에서 유휴 엔비디아 GPU의 VRAM을 리눅스 스왑 공간으로 활용하는 프로젝트가 공개됐다. CUDA 드라이버 API로 VRAM을 할당하고, 이를 NBD 블록 디바이스로 노출해 일반 스왑처럼 쓰는 방식이다. 순차 처리량은 NVMe보다 느리지만, 드문드문 발생하는 스왑 접근에서는 평균 지연 시간이 335마이크로초로 NVMe의 9.05밀리초보다 27배 빠르다는 결과가 흥미롭다.

## 뭐 하는 프로젝트냐

- 리눅스에서 놀고 있는 엔비디아 GPU의 VRAM을 스왑 공간으로 쓰는 프로젝트가 나옴
  - 대상은 하이브리드 그래픽 노트북처럼 메모리는 납땜돼 있어 업그레이드가 안 되고, 엔비디아 GPU는 평소 거의 놀고 있는 환경
  - 화면 출력은 내장 AMD/ATI GPU가 맡고, 엔비디아 VRAM은 비어 있으니 그걸 고우선순위 스왑으로 쓰자는 아이디어임

- 테스트 환경은 꽤 구체적임
  - AMD/ATI + RTX 3070 Laptop 조합
  - 시스템 RAM 16GB, VRAM 8GB
  - 엔비디아 드라이버 580.159.03, 리눅스 커널 6.17, Pop!_OS
  - VRAM 7GB를 스왑으로 할당했고, zram과 SSD 스왑까지 합쳐 전체 주소 지정 가능 메모리가 약 46GB까지 늘었다고 함

- 메모리 압박이 올 때 순서는 이렇게 잡음
  - RAM이 먼저 찬다
  - 그다음 VRAM이 PCIe 경유로 넘치는 데이터를 받는다
  - 이후 zram이 CPU로 압축해 버틴다
  - 마지막으로 SSD 스왑을 쓴다

```mermaid
sequenceDiagram
    participant 커널스왑 as 커널 스왑
    participant 엔비디 as NBD 드라이버
    participant 데몬 as nbd-vram 데몬
    participant 쿠다 as CUDA API
    participant 브이램 as GPU VRAM
    커널스왑->>엔비디: /dev/nbdX에 페이지 쓰기
    엔비디->>데몬: Unix 소켓으로 블록 요청 전달
    데몬->>쿠다: cuMemcpyHtoD 호출
    쿠다->>브이램: 데이터를 VRAM에 저장
    커널스왑->>엔비디: 페이지 읽기 요청
    엔비디->>데몬: 블록 읽기 전달
    데몬->>쿠다: cuMemcpyDtoH 호출
    쿠다->>커널스왑: 데이터를 다시 반환
```

## 구현이 재밌는 이유

- 커널 모듈을 새로 만들지 않는다는 게 핵심 포인트임
  - 작은 데몬이 CUDA 드라이버 API로 VRAM을 할당함
  - 그 VRAM을 NBD(Network Block Device) 프로토콜로 블록 디바이스처럼 제공함
  - 커널의 기본 nbd 드라이버가 붙으면 /dev/nbdX가 생기고, 여기서부터는 일반 스왑 장치처럼 다룰 수 있음

- 데이터 경로는 대충 이런 식임
  - 커널 스왑 서브시스템
  - /dev/nbdX
  - nbd 커널 드라이버
  - Unix 소켓
  - nbd-vram 데몬
  - cuMemcpyHtoD, cuMemcpyDtoH
  - GPU VRAM

- 이 방식의 장점은 유지보수 부담이 작다는 것
  - 엔비디아 커널 심볼에 의존하지 않음
  - 별도 커널 모듈을 작성하거나 유지하지 않음
  - 커널이나 드라이버 업데이트 후 뭔가를 다시 빌드할 필요가 없다는 설명임

> [!TIP]
> 이 프로젝트의 묘미는 “VRAM을 커널에서 직접 매핑하자”가 아니라, 커널이 이미 아는 NBD 경로로 우회했다는 점임. 덕분에 소비자용 지포스 제한을 피함.

## 왜 직접 매핑을 안 했나

- 가장 obvious한 접근은 엔비디아 P2P API로 VRAM 페이지를 BAR1에 고정하는 것임
  - nvidia_p2p_get_pages_persistent로 VRAM을 핀 처리하고 CPU가 ioremap_wc로 직접 접근하는 방식
  - 그런데 기존 프로젝트들이 다 같은 벽에 부딪혔다고 함
  - 소비자용 지포스 GPU에서는 엔비디아 드라이버가 EINVAL을 반환함

- 이 제한은 드라이버 버전 문제가 아니라 제품 등급 문제로 보임
  - persistent와 non-persistent 변형 모두 막힘
  - 플래그 값을 바꿔도 안 됨
  - RM 레벨에서 쿼드로·데이터센터 SKU 쪽으로 게이트된 것으로 설명함

- BAR1 물리 주소를 그냥 ioremap_wc로 직접 매핑하는 방식도 실패함
  - GPU 내부 페이지 테이블에는 약 16MiB 정도의 BAR1만 매핑돼 있음
  - 사실상 디스플레이 프레임버퍼 정도만 보이는 수준
  - 나머지를 읽으면 0이 반환돼서 mkswap은 되는 것처럼 보이지만 swapon에서 스왑 헤더가 없다고 실패함

## 성능은 생각보다 미묘하고, 그래서 더 흥미로움

- 대용량 순차 전송에서는 NVMe가 이김
  - NVMe는 쓰기 2.7GB/s, 읽기 2.9GB/s
  - VRAM NBD는 쓰기 1.1GB/s, 읽기 2.3GB/s
  - 모든 블록이 Unix 소켓과 CUDA 복사 호출을 오가야 해서 사용자 공간 왕복 비용이 생김

- 지속적인 랜덤 I/O도 NVMe가 더 빠름
  - NVMe는 읽기 45.4k IOPS, 쓰기 45.3k IOPS, 평균 지연 343마이크로초
  - VRAM NBD는 읽기·쓰기 모두 28.7k IOPS, 평균 지연 550마이크로초
  - iodepth=32에서 NVMe는 요청을 진짜 병렬로 많이 처리하지만, NBD+CUDA 경로는 데몬을 거치며 직렬화되는 비용이 있음

- 그런데 드문드문 발생하는 접근에서는 그림이 뒤집힘
  - NVMe 평균 지연은 9.05밀리초
  - VRAM NBD 평균 지연은 335마이크로초
  - 평균 기준으로 VRAM이 27배 빠르다고 설명함

> [!IMPORTANT]
> 실제 노트북 스왑 체감은 GB/s 순차 처리량보다 “가끔 발생하는 4KB 페이지 폴트가 얼마나 빨리 돌아오냐”에 가깝다는 게 이 프로젝트의 주장임.

- NVMe가 느려지는 이유로는 APST가 지목됨
  - NVMe 자체는 워밍업 요청에서 약 112마이크로초까지 가능함
  - 하지만 유휴 상태에서 전력 절약 모드로 들어갔다가 깨어나면 약 9밀리초 지연을 먹음
  - 1초에 1번꼴로 발생하는 산발적 스왑 접근에서는 거의 매번 차갑게 깨어나는 셈

- VRAM은 이 시나리오에서 일관성이 강점임
  - 측정 지연은 최소 134마이크로초, 평균 335마이크로초, 최대 490마이크로초
  - 노트북에서 메모리 압박은 지속적인 GB/s 홍수라기보다 개별 4KB 페이지 폴트가 드문드문 오는 상황이 많음
  - 9밀리초는 사용자가 버벅임으로 느낄 수 있지만, 335마이크로초는 훨씬 덜 거슬린다는 논리임

- 설치와 운영 쪽도 꽤 실사용을 의식함
  - 서비스는 설치 후 부팅마다 자동으로 올라옴
  - 요청한 VRAM 크기를 먼저 잡고, 부족하면 512MiB 단위로 줄여가며 가능한 만큼 확보함
  - 전원 상태를 감지해 배터리 사용 시 서비스를 멈추고, AC 전원이 돌아오면 다시 시작하는 옵션도 있음

---
## 기술 맥락

- 이 프로젝트가 고른 핵심 선택은 VRAM을 커널에서 직접 만지지 않고 NBD로 감싸는 거예요. 직접 매핑이 더 멋져 보이지만, 소비자용 지포스에서는 엔비디아 드라이버 제한 때문에 막히니까 현실적인 우회로를 택한 셈이에요.

- NBD를 쓰면 커널 입장에서는 그냥 블록 디바이스처럼 보여요. 뒤에서 데몬이 CUDA API로 VRAM에 데이터를 쓰고 읽는 구조라, 스왑 시스템을 크게 손대지 않고도 VRAM을 저장소처럼 끼워 넣을 수 있어요.

- 성능 해석도 단순히 “NVMe보다 빠르다”가 아니에요. 순차 처리량과 지속 랜덤 I/O는 NVMe가 이기지만, 노트북에서 체감되는 스왑은 드문 4KB 페이지 폴트가 많아서 지연 시간이 더 중요하다는 관점이에요.

- APST 때문에 NVMe가 유휴 상태에서 깨어나는 비용을 먹는다는 점도 포인트예요. VRAM은 그런 전력 상태 전환 지연이 없어 133~490마이크로초 범위로 꾸준히 응답했고, 이게 실제 체감 개선의 근거로 제시돼요.

- 커널 모듈을 안 만든 것도 유지보수 관점에서 큰 선택이에요. 커널이나 엔비디아 드라이버가 바뀔 때마다 깨지는 프로젝트는 오래 쓰기 어렵거든요. 여기서는 표준 nbd 드라이버와 CUDA 복사 API를 써서 그 리스크를 줄였어요.

## 핵심 포인트

- CUDA로 VRAM을 할당한 뒤 NBD를 통해 리눅스 블록 디바이스로 노출하는 방식임
- 테스트 환경은 16GB RAM, 8GB VRAM의 RTX 3070 Laptop이며 7GB VRAM을 스왑으로 할당함
- 전체 주소 지정 가능 메모리는 zram과 SSD 스왑까지 합쳐 약 46GB로 늘었다고 설명함
- 순차 읽기·쓰기와 지속 랜덤 I/O는 NVMe가 빠르지만, 드문 접근 평균 지연 시간은 VRAM이 335마이크로초로 NVMe보다 27배 빠름
- 커널 모듈이나 엔비디아 커널 심볼에 의존하지 않아 커널·드라이버 업데이트 후 재빌드가 필요 없다는 점이 장점임

## 인사이트

아이디어가 완전 실용 괴짜 감성임. NVMe보다 무조건 빠른 스왑을 만들겠다는 게 아니라, 노트북에서 실제로 체감되는 ‘가끔 튀어나오는 4KB 페이지 폴트’를 VRAM으로 받겠다는 관점이 좋다.
