본문으로 건너뛰기
피드

리눅스에서 놀고 있는 엔비디아 VRAM을 스왑 공간으로 쓰는 오픈소스 프로젝트

open-source 약 9분
vote
0
댓글
북마크

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

  • 1

    CUDA로 VRAM을 할당한 뒤 NBD를 통해 리눅스 블록 디바이스로 노출하는 방식임

  • 2

    테스트 환경은 16GB RAM, 8GB VRAM의 RTX 3070 Laptop이며 7GB VRAM을 스왑으로 할당함

  • 3

    전체 주소 지정 가능 메모리는 zram과 SSD 스왑까지 합쳐 약 46GB로 늘었다고 설명함

  • 4

    순차 읽기·쓰기와 지속 랜덤 I/O는 NVMe가 빠르지만, 드문 접근 평균 지연 시간은 VRAM이 335마이크로초로 NVMe보다 27배 빠름

  • 5

    커널 모듈이나 엔비디아 커널 심볼에 의존하지 않아 커널·드라이버 업데이트 후 재빌드가 필요 없다는 점이 장점임

뭐 하는 프로젝트냐

  • 리눅스에서 놀고 있는 엔비디아 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 스왑을 쓴다
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
  • 이 방식의 장점은 유지보수 부담이 작다는 것

    • 엔비디아 커널 심볼에 의존하지 않음
    • 별도 커널 모듈을 작성하거나 유지하지 않음
    • 커널이나 드라이버 업데이트 후 뭔가를 다시 빌드할 필요가 없다는 설명임

💡

> 이 프로젝트의 묘미는 “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배 빠르다고 설명함

중요

> 실제 노트북 스왑 체감은 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를 써서 그 리스크를 줄였어요.

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

댓글

댓글

댓글을 불러오는 중...

open-source

러스트로 다시 만든 로컬 코딩 에이전트 ‘파이’, 자동화까지 노린다

파이는 기존 pi 코딩 에이전트를 러스트로 다시 만든 터미널 기반 AI 코딩 에이전트다. 프로젝트 안에서 파일을 읽고 수정하고, 셸 명령을 실행하고, 세션을 이어가며, 로컬 모델과 여러 모델 제공자를 쓸 수 있다. 단순 채팅 UI가 아니라 트리거, 크론, MCP 알림, 에이전트 간 허브 메시징까지 넣은 로컬 에이전트 런타임을 지향한다.

open-source

윈도우 95 시절 쉘 API로 만든 명령줄 도구, WinUtils 회고

글쓴이가 1996~1997년에 만든 WinUtils는 윈도우 95의 쉘 API를 감싼 얇은 명령줄 도구 모음이었다. 직접 파일 입출력을 구현하는 대신 탐색기와 같은 복사, 이동, 삭제, 휴지통, 확인창 동작을 그대로 활용한 점이 핵심이다.

open-source

Paseo, 여러 코딩 에이전트를 한 화면에서 돌리는 오픈소스 인터페이스 공개

Paseo는 Claude Code, Codex, Copilot, OpenCode, Pi 같은 코딩 에이전트를 한 인터페이스에서 실행하고 관리하는 오픈소스 도구다. 로컬 daemon이 에이전트 프로세스를 관리하고, 데스크톱·모바일·웹·CLI 클라이언트가 여기에 연결되는 구조라 자기 개발 환경 위에서 병렬 작업을 돌릴 수 있다.

open-source

줄리아 반응형 노트북 Pluto가 6년 만에 1.0을 찍었다

줄리아용 반응형 노트북 환경 Pluto.jl이 6년 개발 끝에 1.0을 공개했다. 이번 1.0 자체는 제거된 deprecated 기능 정도로 조용하지만, 재현성, 공유, 반응형 실행, 교육용 기능, 접근성, 편집기 도구까지 지난 몇 년의 누적 개선을 정리한 릴리스에 가깝다.

open-source

괄호가 싫어도 한 번은 봐야 할 작은 리스프, 재닛

글쓴이는 취미 프로젝트용 언어로 작은 리스프 계열 언어인 재닛을 몇 년째 쓰고 있고, 무료 책까지 쓸 정도로 꽂혔다. 핵심은 문법 장난이 아니라 작은 런타임, 네이티브 실행 파일 배포, 파싱 표현 문법, 셸 스크립팅, 매크로, 컴파일 타임 실행이 한데 묶인 실용성이다.