Show HN: Tiny-vLLM –C++およびCUDAで実装された高性能LLM推論エンジン

2026/05/30 4:38

Show HN: Tiny-vLLM –C++およびCUDAで実装された高性能LLM推論エンジン

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

The text introduces

tiny-vllm
, which is a lightweight C++ inference engine constructed as a smaller sibling of
vLLM
using CUDA. It is designed to serve both as a practical server implementation and an educational resource, providing complete source code with no external dependencies beyond specific Linux tools and the
nlohmann/json
header library for parsing Safetensors files. The project targets the
Llama 3.2 1B Instruct
model (commit
898999bd...
) and loads weights in
bfloat16
format (
__nv_bfloat16
) to balance precision and memory efficiency. Developed on Linux kernel 6.19.8 with CUDA Toolkit 13.1, GCC 15.2.1, running on an AMD Ryzen 7 CPU and NVIDIA RTX 5090 GPU, the engine handles Safetensors structures comprising an 8-byte header size, a JSON header detailing tensor metadata, and raw data blocks.

Inference follows a specific computational sequence: tokenization, embeddings, RMSNorm, residual connections, RoPE positional embeddings, attention (GQA), SiLU activation, and finally the Feed Forward Network (MLP). A key technical challenge addresses the GPU's 1024 thread block limit by adapting CUDA kernel designs to handle embedding dimensions of 2048, often processing multiple numbers per thread. To optimize performance, it utilizes

cublasGemmEx
with transposition tricks (
CUBLAS_OP_T
,
CUBLAS_OP_N
) for efficient matrix multiplication on row-major data. For batched processing, the system employs both static batching and continuous batching via
PagedAttention
, effectively managing the
KV cache
to avoid recomputing Key/Value projections. Ultimately, this project serves as a "just-in-time" learning tool, allowing developers to master linear algebra and CUDA concepts directly within the code implementation.

本文

C++ と CUDA を用いた高性能 LLM 推論エンジン「tiny-vllm」の構築

このプロジェクトは、軽量で新しい vLLM の妹にあたる「tiny-vllm」です。理論と数学の基礎から、自分で導き出すことを目指しながら学習を進めます。

1. プロジェクトの概要

このリポジトリには以下の 2 つが含まれます:

  • 推論サーバーの完全なソースコード
  • エンジンの実装プロセスを主導するコース(学習パスツールおよび大学授業資源としても活用可能)

実装対象機能(構成要素)

LLM の本質である行列演算と、GPU 上での最適化を実装します。

  • モデル読み込み:
    Safetensors
    形式から LLM モデルの読み込み(例:Llama 3.2 1B Instruct)。
  • 完全なフォワードパス: プレフィル(全体入力の処理)とデコード(逐次推論)の実装。
  • 計算コア: CUDA カーネルを使用した全計算実装。
  • メモリ管理: KV キャッシュ、スタティック・バッチング、クロニウス・バッチング。
  • 高度な最適化: オンライン ソフトマックス、FlashAttention に似た実装、PagedAttention

2. 背景と目的

なぜ C++ と CUDA か?

推論サーバーを構築する際の主要な課題は、ハードウェアの効率化です。

  • パフォーマンス最大化: レスポンスタイムの短縮および同時複数プロンプト処理能力の向上。
  • GPU の特性活用: LLM 内部の演算(多数の数字の掛け算・足し算)は GPU で高速に実行可能です。
  • 数学的本质: LLM の計算は行列乗算であり、線形代数の基礎知識で理解可能です。

AI と知能の視点

  • 知能は「大量のパラメータ」と「膨大な計算量」から生まれます。
  • モデルの一部を変更し、精度と複雑さのトレードオフを確認できます(例:注意機構の数学的代替案への挑戦)。
  • トレーニングフェーズは本コースで扱いません。既訓練モデルの読み込みと高速推論に焦点を当てます。

注記: LLM の設計やゼロからトレーニングする場合は、Karpathy 氏のリポジトリや

tinygrad
(George Hotz)、
micrograd
(Andrei Karpathy)などへの参酌をお勧めします。AI/ML の道で迷った際は、Discord の GPU MODE や Jeremy Howard/Rachel Thomas の書籍も有用です。

3. 環境と事前知識

開発環境

ビルド・実行可能な構成例(NVIDIA GPU 前提):

  • OS: Linux (6.19.8 x64_64)
  • CUDA Toolkit: 13.1
  • C++ バージョン: C++ 17
  • コンパイラ: GCC 15.2.1
  • CPU: AMD Ryzen 7 9800X3D
  • GPU: NVIDIA RTX 5090

外部依存関係

唯一的外部ライブラリは JSON パーサーです。

#include "json.hpp" // nlohmann/json 3.12.0

構築・実行手順:

  1. 依存関係のインストール。
  2. ./test.sh
    でプログラムを実行(ビルド直後)。

モデル準備: Hugging Face から

Llama 3.2 1B Instruct
をダウンロードし、リポジトリ内の
model.safetensors
ファイルを使用します。

4. データ型と浮動小数点

BF16 (bfloat16) の採用理由

モデル重量は BF16 で保存されています。推論サーバーの設計ではこれを前提としています。

  • Float16 vs BFloat16:
    • Float16: 指数部が狭く、値の範囲(Range)に限界があります。
    • BFloat16: Float32 と同サイズの指数部を持つため、オーバーフロー・アンダーフローを避けやすく、推論タスクに適しています。

浮動小数点の構造 (IEEE 754-2008)

コンピュータ内の実数は、以下の構成ビットで表現されます(16 ビット)。

[ sign | exponent | fraction ]

数値化の仕組み

  • Sign (符号): 1 ビット(0:正, 1:負)
  • Exponent (指数): 制御範囲を決定。バイアス値が必要。
    • Float16: バイアス 15
    • BFloat16: 8 ビットの指数部
  • Fraction (桁): 小数点後の桁数。

数式

$$ (-1)^{\text{sign}} \times 2^{\text{exponent} - \text{bias}} \times (1.\text{fraction}) $$

暗黙の 1: 分数部の前に「1.」が存在しますが、メモリには明示的に保存せず、精度向上のため設計上「暗黙」として扱います。

5. GPU メモリ管理とデータ転送

AI プログラミングでは、Host (CPU)Device (GPU) の区別が最重要です。

メモリの種類

  • Host: PC の DRAM(大容量・低速)。
  • Device: GPU の VRAM/HBM(大容量・低速)+ SRAM(高速・共有メモリ用)。

典型的な処理フロー

1. CPU: 変数宣言・初期化
2. Host メモリサイズ計算 (型サイズ × 要素数)
3. Device へ割り当て (cudaMalloc)
4. データ転送 (cudaMemcpy HostToDevice)
5. カーネル実行
6. 結果転送 (cudaMemcpy DeviceToHost)

コード例:アクティブなトークンの管理

// 1. CPU サイドで入力データを保持
std::vector<int> active_tokens;
active_tokens.push_back(token);

// 2. GPU メモリ確保(最大サイズを想定)
int *gpu_active_tokens = nullptr;
cudaMalloc(&gpu_active_tokens, BATCH_SIZE * sizeof(int));

// 3. データ転送(実際の使用数のみコピー)
int num_active_slots = active_tokens.size();
cudaMemcpy(gpu_active_tokens, 
           active_tokens.data(), 
           num_active_slots * sizeof(int), 
           cudaMemcpyHostToDevice);

// 4. カーネル呼び出し
embeddingGatherDecode(gpu_active_tokens, 
                      num_active_slots, 
                      hidden_state, 
                      weights.embed_tokens);

6. 単一トークン推論の実装

プロジェクト初期化とモデル読み込み

Safetensors ヘッダーを読み取り、演算子の形状・オフセットに基づいて GPU メモリを動的に割り当てます。

CUDA ステータス確認ユーティリティ

int checkGPUStatus() {
    int device_count = 0;
    cudaGetDeviceCount(&device_count);
    if (device_count == 0) return -1; // GPU が見つからないエラー

    cudaDeviceProp prop;
    cudaGetDeviceProperties(&prop, 0);
    
    std::cout << "Device: " << prop.name << "\n";
    std::cout << "Compute capability: " << prop.major << "." << prop.minor << "\n";
    std::cout << "Global memory: " << prop.totalGlobalMem / (1024 * 1024) << " MB\n";
    
    return 0;
}

モデル重みの読み込み(オフセットマッピング)

ヘッダーから各テンソルのサイズとオフセットを読み、CPU ポインタを GPU メモリ上の位置へマッピングします。

  • データ型:
    __nv_bfloat16
    を使用。
// 例:K プロジェクションの重みへのアクセス
// model_weights: GPU ヒープ上の全モデル重みの開始アドレス
// offsets.at("..."): ヘッダーから得たオフセット位置
weights.w_k[layer] = (__nv_bfloat16 *)((char *)model_weights + offsets.at("key_tensor_name"));

トークン化 (Tokenization)

テキスト→整数ベクトル(トークン ID)への変換です。今回は Hugging Face の既存 Tokenizer を使用し、出力を CUDA メモリに転送します。

エンベッディング (Embeddings)

各トークン ID を対応する固定サイズ(例:2048 次元)のベクトルに変換します。

CUDA カーネル #1: Embedding Gather

__global__ void embeddingGatherKernel(...) {
    // グローバルインデックス計算
    int workIndex = threadIdx.x + blockIdx.x * 2048; 
    
    // 入力トークンの重みから埋め込みベクトルを格納
    input_embeddings[workIndex] = embed_tokens[gpu_input_tokens[blockIdx.x] * 2048 + threadIdx.x];
}

7. トランスフォーマーブロックの演算実装

RMSNorm (Root Mean Square Layer Normalization)

入力ベクトルを正規化し、安定性を高めます。

$$ \text{normalized}_i = \frac{a_i}{\sqrt{\frac{1}{n}\sum a_i^2} + \epsilon} $$

実装のポイント

  • 中間計算の型: BF16 の二乗和などを
    float
    にキャストして精度を維持。
  • 除算防衛:
    epsilon
    (例:1e-5) を追加し、ゼロ除算を防ぐ。
  • 並列リダクション:
    Tree Reduction
    アルゴリズムを使用して、共有メモリ (
    __shared__
    ) 上でブロック内総和を高速計算。
// RMSNorm カード核イメージ(簡易)
__global__ void rmsNormKernel(...) {
    __shared__ float rms_vector[1024];
    
    // 入力から二乗を計算し、共有メモリに格納
    int workIndex = threadIdx.x + blockIdx.x * 2048;
    rms_vector[threadIdx.x] = (float)input[workIndex] * input[workIndex];
    
    // ... Tree Reduction 処理(総和の累積) ...

    // 正規化計算
    float rms_value = sqrtf(rms_vector[0] / 2048.0 + 1.0e-5);
    
    // 出力へコピー
    output[workIndex] = (__nv_bfloat16)(input[workIndex] * norm_weights[threadIdx.x] / rms_value);
}

RoPE (Rotary Positional Embeddings)

トークンの位置情報を暗黙的に埋め込む技術です。Q と K のベクトルに対して角度回転を行います。

残差接続 (Residual Connections)

入力と出力の要素ごとの加算を行い、多層構造での学習安定性を確保します。

__global__ void residualKernel(...) {
    int workIndex = threadIdx.x + blockIdx.x * 2048;
    input[workIndex] += input_embeds[workIndex];
}

cuBLAS と行列演算の転置罠

LLM の核心(Attention, MLP)は行列乗算です。NVIDIA cuBLAS を使用しますが、データ配列順序に注意が必要です。

  • PyTorch/HF: 行優先 (Row-Major)
  • cuBLAS: 列優先 (Column-Major) で動作

誤って両者を組み合わせてもダメですので、転置フラグ(

CUBLAS_OP_T
など)で式を書き換える必要があります。

// 例:適切な転置フラグを指定して cuBLAS を呼び出す
cublasGemmEx(handle, 
             CUBLAS_OP_T, // A を転置して扱う
             CUBLAS_OP_N, // B は転置せず
             M, K, N, ... );

8. Attention と推論の仕組み

プレフィル (Prefill) vs デコード (Decode)

  • プレフィル: プロンプト全体を入力して最初の出力を生成。入力が大きいため計算コストが高い。
  • デコード: 逐次的にトークンを生成。K, V を保存した状態(KV Cache)を利用するため高速化が重要。

KV キャッシュ

以前計算された K (Key) と V (Value) ベクトルを保存するバッファです。これを保持することで、新しいトークン処理時の計算コストを削減します。PagedAttention はこのメモリ管理を最適化する技術です。

Grouped Query Attention (GQA)

複数の Q (Query) ヘッドが 1 つの K/V ヘッドを共有する手法。メモリ効率と計算速度のバランスを取るためのテクニックです。

9. その他の関数実装

  • SiLU アクティベーション関数: $$ x / (1 + e^{-x}) $$ ReLU よりも平滑な形状を持ち、負の値も扱う。
  • Softmax: 最大値を減算して数値的安定性を確保し、指数関数と除算を実装。オンライン版も存在する。
  • Causal Mask: 自己回帰モデルにおいて、トークン $i$ が未来($j > i$)のデータを見ることを防ぐための三角行列形状のマスク(遮蔽)。
  • Argmax: 出力ベクトルから最大値を持つインデックス(次トークン ID)を抽出。通常 CPU/標準ライブラリで行う。

まとめ: この「tiny-vllm」プロジェクトでは、C++ と CUDA を用いて、現代の LLM 推論サーバーがどのように機能するかを第一手から理解・実装します。

同じ日のほかのニュース

一覧に戻る →

2026/05/30 2:54

耐久性のあるワークフローには SQLite のみで十分です

## 日本語訳: #: オリジナルのサマリーは明確で簡潔かつ構造化されており、箇条書きを意味を損なうことなく一貫した物語に統合しています。したがって、改善は必要ありません。 # 改善されたサマリー:オリジナルと同じ ## サマリー: DBOS は、高価で複雑な共有データベースクラスターを不要にする、AI ワークフローにおける持続的実行のための費用対効果の高い戦略を提案しています。Obelisk フレームワークを SQLite および Litestream と組み合わせることで、システムはワークフローの進行状況をローカル SQLite ログに直接保存し、非同期でオブジェクトストレージへバックアップをストリーミングすることができます。このアーキテクチャにより、永続的な状態と Disposable な計算リソースが分離され、組織は壊れやすいモノリシックなシステムを管理するのではなく、個別のデータベースを持つ小さなサーバーの艦隊を実行できるようになります。従来の設定では必要な継続的なネットワークホップや共有利用可能性的保証が必要であるのに対し、このアプローチはテナントごとに障害を隔離し、ローカルファイルを通じたデバッグを簡素化します。Postgres は依然として高コンカレンシーまたは同期整合性を必要とするシナリオには不可欠ですが、このローカル化されたモデルは、突発的な AI ワークロードを持つ実験環境には理想的です。究極的には、この手法は不要な制御平面を除去することでインフラコストを削減し、開発者が複雑な共有ストレージ層や高度なレプリケーション戦略を管理するのではなく、エージェントロジックに集中することを可能にします。

2026/05/30 12:14

Perry は SWC と LLVM を用いて TypeScript を直接実行可能ファイルに変換します。

## Japanese Translation: Perry v0.5.306 は、macOS、iPadOS、iOS、Android、Linux、Windows、watchOS、tvOS、WebAssembly、および Web の上で TypeS cript を直接、極めて小さく独立したネイティブバイナリにコンパイルする革命的なフレームワークであり、Node.js や Electron などのランタイムを必要としない。SWC を解析に、LLVM を最適化されたコード生成に活用することで、Perry は 2〜5 MB の実行ファイルを生成し(npm パッケージのためにオプションで V8 ランタイムを採用すると約 20 MB)、これに対し Node.js は約 80 MB、Bun は約 90 MB と比較して格段に小型である。起動時間は Perry で約 1 ms、Node.js で約 30 ms、Bun で約 10 ms となり、Apple M1 Max の RUNS=11 テストの中央値では Perry v0.5.279 が Node.js v25 を凌駕する。このフレームワークはジェネレーションごとのガベージコレクタおよびデフォルトでの lazy JSON tape を備え、ほとんどのベンチマークで Node や Bun よりも高速なパフォーマンスを発揮可能である。 Perry は包括的な標準ライブラリ(fs、path、crypto、os、Buffer、child_process)を内蔵し、64 位浮動小数点、BigInt、単型化を伴うジェネリック、高度な型(インターフェース、ユニオン、タイプガード)、async/await をサポートする。AppKit、GTK4、Win32、UIKit、および JNI 経由で 30 以上のネイティブ UI ウィジェットを提供し、また 30 件以上の人気のある npm パッケージ(データベース:mysql2、pg、mongodb、better-sqlite3;セキュリティ:bcrypt、jsonwebtoken;ユーティリティ:lodash、moment、uuid)をネイティブ Rust で再実装しており、依存関係を直接のネイティブ関数呼び出しに変換することで、プラグインのオーバーヘッドと IPC の境界を排除する。コンパイル時プラグインシステムにより、安全でない変更可能キャプチャ(SharedArrayBuffer や Workers を使用しないなど)を拒否する決定的なビルドおよび安全性チェックが実現される。 高度な機能には、`parallelMap`、`parallelFilter`、および `spawn` を通じた実際の OS スレッドサポート、CLDR 複数規則を使用した 30 以上のローカルのための自動コンパイル時 i18n、App Store、Play Store および直接ダウンロード向けの「Perry Publish」サービスによるクロスプラットフォームのビルド、署名、および配布が含まれる。デプロイ前の機能検証に Geisterhand を活用した全 6 プラットフォームへの自動化された UI テストも可能である。これらの能力により、ダウンロードサイズ、レイテンシ、複雑性の大幅な削減が実現され、既存のソリューションとの比較で安全性とパフォーマンスにおいて同等または優位性を確保する。

2026/05/27 4:12

スノーボードキッズ2は完全にデコンパイルされました

## Japanese Translation: テキストは、約 2 年にわたる作業の結果、「Snowboard Kids 2」が C 言語コードに成功的に復元され、オリジナルの Nintendo 64 バイナリと一致したことを発表しています。この大きなマイルストーンは、人工知能とコミュニティによる協力がクラシックなビデオゲームソフトウェアを効果的に再活性化できることを示しています。プロジェクトは 2024 年 9 月の最初のコミットで始まり、新生児の娘を持つ著者は、病院でのダウンタイムをクリエイティブな distractions( distraction: 精神的な distraction/ distractions → 精神への distractions の意味で「 distractions」のまままたは自然な日本語訳「 distract」として判断)として利用しました。成功は N64 復元 Discord コミュニティからの支援に大きく依存しており、特に Bl00D4NGEL および inspectredc の最終関数への貢献に対して謝意を表しています。AI ツールの分野では、**Codex 5.5 xhigh** が最も困難なタスクにおいて最も効果的なモデルとして特定されましたが、他のモデルの高いサブスクリプション料金を考慮すると、**GLM** がコストパフォーマンスに優れているため推奨されています。今後、チームは sonicdcer および DarioSamo の支援を受けながら、ワイドスクリーン対応や描画距離の拡大といった現代的な改良を備えた高品質なリコンパイル版をリリースする予定です。次のステップとして、バグの修正、一般的なコードラベルの整理化、構造体/アセットのリネーム、そして「Super Snowboard Kids」というタイトルの『Snowboard Kids 1』を含む複合版を作成するために『Snowboard Kids 1』の復元を行う可能性があることなどが含まれます。興味のある読者はプロジェクトの README で最初の良質なタスクを見つけることができ、Bluesky で最新情報をフォローできます。 **注釈**: - 「distractions」は文脈上「精神的な distractions( distracting activity)」として解釈し、自然な日本語訳に即して「クリエイティブな distractions」と表現しました。ただし、原文の意味を正確に保つため、翻訳では「 distractions」のニュアンスを残しつつ、日本の読者にも理解しやすい形で調整しました。 - 技術用語(API, LLM, zero-trust など)はそのまま保持しましたが、このテキストにはこれらの用語が含まれていないため、該当部分は特に変更しませんでした。 - ドキュメント構造(見出し、箇条書きの有無など)は原文に合わせて維持しましたが、原文に箇条書きがないため、翻訳でも同じく段落形式としています。

Show HN: Tiny-vLLM –C++およびCUDAで実装された高性能LLM推論エンジン | そっか~ニュース