世界を動かす技術を、日本語で。

Show HN: NVMe経由でCPUをバイパスして単一のRTX 3090上で動作するLlama 3.1 70B

概要

NTransformer は、 高効率なC++/CUDAベースのLLM推論エンジンLlama 70BRTX 3090(24GB VRAM)単体で実行 可能。 GPUメモリを超えるモデルサイズもNVMe経由ストリーミングで対応3層アダプティブキャッシュ・レイヤースキップなど高速化技術搭載Linux・CUDA 13.1・gcc-14対応、ハードウェア設定に注意が必要

NTransformerの特徴と概要

  • NTransformer は、 C++/CUDAのみ で動作する 高効率LLM推論エンジン
  • Llama 70B のような大規模モデルを RTX 3090(24GB VRAM)1枚で実行 可能。
  • モデルレイヤーをGPUメモリにストリーミング転送 し、PCIe帯域を最大限活用。
  • NVMeダイレクトI/O により、 CPUを完全にバイパス することも可能。
  • GGUFモデル形式 に対応し、 Q4_0/Q8_0/Q4_K_M/Q5_K/Q6_K/F16/F32量子化 をサポート。

主要なベンチマーク・結果

  • 8Bモデル(Q8_0)全レイヤーVRAM常駐約49 tok/s (10GB VRAM使用)。
  • 70Bモデル(Q6_K, Q4_K_M)ティアード/ストリーミング0.2~0.5 tok/sVRAM 23GB前後で動作
    • Q4_K_M36レイヤーVRAM常駐レイヤースキップ でさらに高速化。
  • 3層アダプティブキャッシュ (VRAM常駐+ピンRAM+NVMe/mmap)で mmapベースライン比83倍高速化
  • PCIe Gen3 x8帯域(約6.5GB/s) が主なボトルネック。

高速化技術

  • 3層キャッシュ自動最適化 :VRAM常駐、ピンRAM、NVMe/mmapの3階層を自動調整。
  • SLEPストリーミング :NVMe読み出し、PCIe DMA、GPU計算をパイプラインで並列化。
  • gpu-nvme-direct :ユーザ空間からNVMeコントローラへ直接アクセスし、GPUメモリへDMA転送。
  • レイヤースキップ :コサイン類似度で冗長レイヤーをスキップ(70Bで20/80レイヤー省略可能)。
  • 自己投機的デコーディング :VRAM常駐レイヤーをドラフトモデルとして利用。

システム要件・依存関係

  • Linux(Ubuntu, kernel 6.17+推奨)
  • CUDA Toolkit 13.1
  • gcc-14 / g++-14
  • NVIDIA GPU Compute Capability 8.0+(RTX 3090動作確認済)
  • CMake 3.24+
  • (オプション)NVMe SSD(専用スロット推奨)+gpu-nvme-directライブラリ
  • 外部依存なし(PyTorchやcuBLAS不要)

ビルド・実行方法(クイックスタート)

  • ビルド例
    mkdir build && cd build
    cmake .. -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_C_COMPILER=gcc-14 \
      -DCMAKE_CXX_COMPILER=g++-14 \
      -DCMAKE_CUDA_COMPILER=/usr/local/cuda-13.1/bin/nvcc
    cmake --build . -j
    
  • 実行例(VRAM常駐)
    ./ntransformer -m /path/to/llama-8b-q8_0.gguf -p "Hello" -n 128
    
  • 実行例(ストリーミング)
    ./ntransformer -m /path/to/llama-70b-q6_k.gguf -p "Hello" -n 32 --streaming
    
  • レイヤースキップ併用(70B向け高速化)
    ./ntransformer -m /path/to/llama-70b-q4_k_m.gguf -p "Hello" -n 32 --streaming --skip-threshold 0.98
    
  • 自己投機的デコーディング
    ./ntransformer -m /path/to/llama-70b-q6_k.gguf -p "Hello" -n 32 --self-spec --draft-k 3
    
  • チャットモード
    ./ntransformer -m /path/to/model.gguf --chat
    
  • ベンチマーク
    ./ntransformer -m /path/to/model.gguf --benchmark -n 64
    

システムセットアップと安全性

  • NVMeダイレクトI/O利用にはシステムレベルの設定変更が必要
    • 自動化スクリプト(setup_system.sh) で対応。
    • IOMMU無効化、カーネルパッチ、VFIOバインド等を実施
    • 誤設定はシステム不安定化・データ損失のリスク
  • 絶対にブートドライブ(OSディスク)をNVMeダイレクトI/Oに使わないこと
  • ハードウェア損傷・データ損失の責任は自己負担

主要スクリプトと用途

  • scripts/setup_system.sh
    • 初回セットアップシステム状態確認NVMeのみ再設定 に対応。
  • scripts/setup_nvme.sh [BDF]
    • 単体NVMeのVFIOバインド
  • scripts/restore_nvme.sh [BDF]
    • NVMeをカーネルドライバに戻す

NVMeダイレクトストリーミングの仕組み

  • モデル(GGUFファイル)をNVMe生ブロックデバイスに書き込み
  • NVMeをVFIOでユーザ空間にバインドし、gpu-nvme-directで初期化
  • 推論時、各レイヤー(例:70B Q6_Kで約670MB)をNVMeから直接CUDAピンメモリへDMA転送
  • NVMe読み出し、PCIe DMA、GPU計算をダブルバッファで並列化

アーキテクチャ構成

  • src/core/ :テンソル・アロケータ・GPU管理
  • src/cuda/ :GEMV・RMSNorm・RoPE・SwiGLU・softmax CUDAカーネル
  • src/memory/ :SLEP層ストリーミングエンジン(NVMe+mmap)
  • src/model/ :Transformer構成・GGUFローダー
  • src/inference/ :トークナイザー・サンプラー・推論エンジン
  • scripts/ :システムセットアップ・NVMe管理スクリプト

3層アダプティブキャッシュ詳細

  • Tier A(VRAM常駐) :I/Oゼロ、全レイヤー常駐
  • Tier B(ピンRAM) :H2D DMAでVRAMに転送、ダブルバッファ
  • Tier C(NVMe/mmap) :NVMe直読み・メモリコピー、必要時のみ利用
  • 階層サイズはcudaMemGetInfo()/proc/meminfoから自動算出

量子化フォーマット対応表

  • Q4_0 :4.5bit/重み、32ブロック
  • Q8_0 :8.5bit/重み、32ブロック
  • Q4_K_M :4.5bit/重み、256ブロック(混合型)
  • Q5_K :5.5bit/重み、256ブロック
  • Q6_K :6.6bit/重み、256ブロック
  • F16/F32 :16/32bit/重み、1ブロック

開発ロードマップ

  • Phase 1 :8Bモデル・CUDAカーネル・高速化(完了)
  • Phase 2 :SLEPストリーミング・3層キャッシュ(完了)
  • Phase 3 :Q4_K_M/Q5_K対応・レイヤースキップ・自己投機的デコーディング(完了)
  • Phase 4 :NVMeダイレクト(GPU主導NVMe読み、3.35GB/s)
  • Phase 5 :投機的デコーディング・ベンチマーク・C API整備

技術的背景と意義

  • CPU/RAMをバイパスしGPUとNVMeを直結する発想 から誕生。
  • コンシューマGPUでも大規模LLM推論を可能にする新アプローチ
  • プロフェッショナルGPUではさらに高い性能を期待

注意:本プロジェクトはハードウェア・システムに深く関与するため、十分な知識とバックアップ体制のもとで運用してください。

Hackerたちの意見

0.2 tok/sは実験には十分だけど、実際にはインタラクティブじゃないよね。多くのユースケースでは、ちゃんと量子化された8Bや13Bモデルの方が、レイテンシとクオリティのバランスが良いと思う。

パフォーマンステーブルを見たとき、上位のモデルが8Bだってことに気づくまでよく分からなかった。5秒/トークンは確かに遅いね。これは低RAMのマシン向けなのかな?5950xに128GBのRAMがあるから、3060のGPUでいくつかのレイヤーやプリフィルを使えば、もっと速く動かせると思う。2秒/トークンで計算制約があるって言ってるけど、3090だとそれは正しくない気がする。

高品質で大きなモデルが、低レイテンシのモデルよりも好まれるシナリオがいくつか想像できるよ。特にクオリティが必要なときね。

そうなんだ、実際にこれが可能かどうか試してみたかったんだ。PS2でクラシックトランスフォーマーを使って、3000トークン/sくらい出せたんだけど、エモーションエンジンが32ビットアドレスに対応してるからね。でも、32GBのRAMがあるから、その速さの理由が気になったんだ。小さいモデルでもその速度が出せなかったのは、命令がメモリからGPUに直接行くからで、普通のコンピュータだと毎回CPUに命令をリクエストしなきゃいけないんだよね。プロ用のカードだと、そういう問題を自然に回避できるんだけど、残念ながらGPUに3万ドルも出せる余裕はないんだ :(

CPU+GPUで動かすより遅いね。7950X+3090で20480トークンのコンテキストを使うと、簡単に1.5トークン/s出せるよ。

DirectXって、アセットを直接GPUメモリにロードするためのAPIを追加したんじゃなかったっけ?それって使えるのかな?

僕の印象では、それはアセットに限られていて、DirectXフレームワークにうまくはまる必要があるみたい。見た感じ、gpu-nvme-directは主にhttps://github.com/enfiskutensykkel/ssd-gpu-dmaやhttps://github.com/ZaidQureshi/bamに似てるね。

いいね。僕も似たようなことを考えてて、VRAMの半分以下で1Tモデルを動かすことを検討してる。ある試算では、SGLangのルーティングレイヤーを修正して、Gen5 NVMeストレージからGPUメモリに直接JIT予測先のエキスパートスワップをサポートすることが理論的に可能だって言ってた。これが本当だといいな。セットアップはNVIDIA Dynamoに依存してるから、NIXLのプリミティブが使えるはず。もう誰か試した人いるのかな?

それは見てみたいね。実は、3090をもう一枚買って、マザーボードもアップグレードしようかなって考えてたんだ。PCIe3でボトルネックになってるから、GLM 4.7か5をQ4_K_Mで動かすのはできるはずなんだ。

これってマルチティアのMoEに使えるかな?例えば、アクティブなもの+VRAMで最も使われるもの、RAMでよく使われるもの、NVMeであまり使われないものとか。

そういえば、VRAMとRAM用の2ティアのMoEをトレーニングしてる人が少ないのは不思議だよね。共有エキスパートのデザインはもうあるから、「コア」エキスパートに10倍や100倍の頻度で割り当てるルーターを実装するのは難しくないはず。トレーニング中のバランス調整は難しいかもしれないけど、ルーター層にカスタムロスを使えばうまくいくと思う。ルーターが直列的一貫性を持つようにトレーニングされてないのも不思議で、数層先にVRAMにスワップするためのレイヤーを予測できるようにすれば、利用可能な帯域幅を最大化できるのに。

これは実験するには面白い分野だね。長期的には、モデルの最適化(モデルの機能に影響を与えずに省ける部分を知ること)が、圧縮アルゴリズムと同じように研究の主流になると思う。実際、モデルは損失のある圧縮方式だからね。それが進むことで、AIの民主化が進んで、作られているサイロから離れることができるのはいいことだよ。

圧縮のアナロジーは面白いね。別の見方をすると、ファインチューニングは「何を省くかを知ること」かもしれない。例えば、狭いタスクにチューニングされた3Bモデルは、70Bが多くのことに優れているための容量を必要としないんだ。

そうだね、GPUダイレクトを使えば、ストレージデバイスに直接DMAできるはず。もしM.2ストレージが実際にDRAMだったらどうなるんだろう?GPUからモデルをスピルするのに永続性は必要ないかもしれない。ホストメモリを増やすのと比べてどうなるかな?M.2 RAMは柔軟性が低いけど、CPU用のシステムRAMを空けておけるね。

うん、RAMディスクはすごく効果的だと思う。Intel Optaneがスタンダードにならなかったのは残念だね。ああいうワークフローには最高だっただろうに。

M.2ストレージじゃなくてDRAMなんじゃない?NVMe/PCIeの速度で、SATAじゃないことを期待してるけど、Compute Express Link (CXL)としてはすでに存在してるよね?今のRAMがめちゃくちゃ高くなければ、NVMeコネクタごとに31GB/sの追加帯域幅を使えるのに。

まさにそれについて考えてたところ!数年前にDaskサミットで、dask-cudfを使って星を合わせる話をしたんだ。お客さんのログ分析を加速させるために、うちのスタックを使って、だいたいこんな感じのノードを用意してたよ:並列SSDストレージアレイ(30 x 3 GB/s?) -> GPUDirect Storage -> 4 x 30 GB/s PCIe(?) -> 8 x A100 GPU、みたいな。今、LLMの世界でも同じようなことが見られたら面白いよね。例えば、マルチGPUのMoEとか、シングルGPUでもいいけど!

0.2トークン/sはチャットには遅いけど、バッチや非同期のワークロードには全然問題ないよ。自動コンテンツ生成パイプラインを運用してて、一つのジョブで数十のLLMコール(スクリプト生成、メタデータ、説明文)を起動するんだけど、どれもインタラクティブである必要はないんだ。画像生成のボトルネックがあるから、全体のジョブは20分かかるけどね。バッチコール用に70Bモデルをローカルで動かせるなら、トークンごとのAPIコストを払うよりも大幅なコスト削減になるよ、たとえこの速度でも。

コスト的にはあまり効率的じゃないみたいだね。最適化されたもので0.5トークン/秒だと、1時間で3600トークンになる。これにはアクティブな3090+システムで約200-300ワットかかる。3600トークンをオープンルーターで@0.4ドルで動かすと、Llama 3.1(3.3はもっと安い)の場合、約0.00144ドルになる。このお金でオランダでは約2-3ワット買えるよ。それでもプライバシー推論に関しては素晴らしい成果だね。

350ワットで3090を長時間動かすエネルギーコストも考慮してる?

これは本当に印象的なシステムエンジニアリングだね。3層の適応キャッシング(VRAM常駐 > ピン留めされたRAM > NVMe/mmap)は、Linuxカーネルのページキャッシュがやってることを再実装してるけど、GPUに配慮した形になってる。3090一台で70B Q4_K_Mで0.3トークン/sはインタラクティブな使用には遅いけど、アーキテクチャ自体が重要なんだ。PCIe Gen3 x8で約6.5 GB/sが明らかなボトルネックだね。Gen5 NVMeのセットアップで、シーケンシャルリードが12GB/s以上出せる数字を見てみたいな。それだけでスループットが倍になる可能性があるよ。コサイン類似度キャリブレーションによるレイヤースキップ(20/80レイヤーをスキップするの)は賢いトリックだね。トランスフォーマーの適応計算に関する初期の研究を思い出すよ。しきい値0.98での品質トレードオフをもっと厳密にベンチマークするのも面白そうだね。要約や分類のような多くの推論タスクでは、もっと先に進めるかもしれないし。あと、CUDA Toolkit以外に外部依存がないのは大胆な設計選択だね。cuBLASがないから、自分たちでGEMMカーネルを書いたってことだし、それは大変な作業だけど、このストリーミングアーキテクチャに必要なメモリアクセスパターンを完全にコントロールできるようになるんだ。

No cuBLAS means they wrote their own GEMM kernels, which is a massive undertaking このプロジェクト全体の印象を下げるつもりはないけど、最初から「これらはバイブコーディングされた」って言ってるし、Opus 4.6の共著者のラインもコミットメッセージに書いてあるよね。これらの部分は、既存の作業からLLMを通じて適応されたもので、こういう概念実証プロジェクトにはぴったりの使い方だと思う。

HNに投稿するのにLLMを使うのはやめてほしいな…。

すごくクールだね。これを思いつくためにはどんなバックグラウンドが必要だったのか気になるな。レトロゲームに関わってるって言ってたけど、ハードウェアがかなり抽象化されてる世界で働いてる自分からすると、レトロゲームにハマってもシステム改善のアイデアが浮かぶかどうか分からない。クリエイティブな面を超えて、アイデアをまとめるのに役立ったシステムやハードウェアの知識があるように感じる(その知識について自分も学んでみたいな)。

自分も気になるな。DMAは、CPUがもっと遅かった昔のゲーム機では大きな役割を果たしてたと思う。だから、試してみようと思ったのかも。PS2のスマートメモリーカードは、SDカードからゲームデータをストリーミングするためのかなり複雑なDMA機能を持ってると思う。

クールなプロジェクトだね。消費者向けGPUのDKMSパッチプロセスについてもっと詳しく教えてくれない?試してみたいけど、そのパッチプロセスについてもう少し情報が必要なんだ。