744B パラメータモデルを 25GB RAM で動かす colibrì を試してみた

colibrì は、744B パラメータの MoE モデル GLM-5.2 を、GPU なしで RAM 25GB 程度の一般的なマシン上で動かすために書かれた C 製の推論エンジンです。アテンションや共有エキスパートなど常に使う部分だけを RAM に常駐させ、21,504 個あるルーティング対象のエキスパートはディスクからストリーミングで読み込むことで、370GB 相当のモデルを 10GB 程度の RAM で扱えるようにしています。この記事では、GitHub で公開されている JustVugg/colibri を実際に手を動かして試してみます。

colibrì とは何か

GLM-5.2 は 744B パラメータの Mixture-of-Experts(MoE)モデルですが、1 トークンあたり実際に計算へ使われるのは 40B パラメータ程度です。しかもトークンごとに切り替わる「ルーティングされたエキスパート」の分だけを見ると、11GB ほどしか変化しません。colibrì はここに着目し、常に使うアテンションや共有エキスパートなど密な部分(dense、約 17B パラメータ)を int4 で RAM に常駐させています(約 9.9GB)。一方、ルーティング対象のエキスパートは合計 21,504 個あり、1 個あたり約 19MB・ディスク上の合計は約 370GB です。これらをレイヤーごとの LRU(使われなくなったものから捨てるキャッシュ管理方式)キャッシュでディスクからストリーミングするという設計を取っています。

エンジン本体は c/glm.c という 1 ファイル約 2,400 行の C コードで、BLAS もランタイムの Python も GPU も要求しません(ピン留めエキスパート用の opt-in な CUDA 層は別途あります)。MLA(圧縮 KV キャッシュ)や DSA(スパースアテンション)、MTP(Multi-Token Prediction、複数トークン先読みによる投機的デコード)、int4/int8/int2 の量子化カーネルなど、GLM-5.2 の実装上の細かい仕様にかなり忠実に作り込まれているのが特徴です。2026 年 7 月上旬に公開されたばかりのプロジェクトですが、執筆時点ですでに 2,000 を超える star を集めています。ライセンスは Apache 2.0 です。

検証環境を用意する

README の「Got a better machine? Try it」の節には、動作要件として Linux(または WSL2)、OpenMP 対応の gcc、AVX2、RAM 16GB 以上、そして int4 変換済みモデル用に約 370GB のローカル NVMe が挙げられています。今回は以下のMacBook Pro (M4 Max)で動作させます。

$ sysctl hw.memsize hw.ncpu
hw.memsize: 51539607552
hw.ncpu: 16

51,539,607,552 バイト、つまり 48GB の統合メモリと 16 コアなので、RAM 要件には余裕で収まっています。作業ディレクトリを作ってリポジトリを clone します。

mkdir -p ~/tmp/colibri-test && cd ~/tmp/colibri-test && \
  git clone --depth 1 https://github.com/JustVugg/colibri.git && \
  cd colibri/c

以降のコマンドはこの colibri/c ディレクトリで実行しています。

ビルドする

Apple の clang は -fopenmp を単体では解釈できないため、libomp を先に入れておきます。

brew install libomp

これで setup.sh を実行すると、ビルドとセルフテストが走ります。

./setup.sh
🐦 colibrì — setup
  clang: Apple clang version 21.0.0 (clang-2100.1.1.101) · 16 core
  OpenMP: ok (libomp)
  compilo (ARCH=native)…
  RAM: 51 GB   (più RAM = più expert in cache = più veloce)

pronto. Prossimi passi:
  ./coli build           # (gia' fatto)
  ./coli convert --model /percorso/NVMe/glm52_i4     # genera il modello int4 (ore)
  ...

ログがイタリア語で出てくるのは colibrì(イタリア語でハチドリ)という名前どおり、作者がイタリア語話者だからのようです。コード中のコメントも随所でイタリア語と英語が併記されていて、後述するエラーメッセージも基本イタリア語で出力されることがわかりました。

ビルド自体は問題なく完了しました。ただしこの時点では glm_tiny(自己検証用の小さなオラクルモデル)がまだ存在しないため、セルフテストの行だけはスキップされています。

セルフテストでアーキテクチャの正しさを検証する

colibrì の README には「transformers オラクルに対してトークン完全一致で検証済み(teacher-forcing 32/32、greedy 20/20)」という強気な主張があります。これは c/tools/make_glm_oracle.py というスクリプトで検証可能で、実際の GLM-5.2 と同じアーキテクチャ(glm_moe_dsa)を持つが層数・エキスパート数を大幅に減らしたランダム重みモデルを transformers で生成し、その forward 結果を ref_glm.json として保存してくれます。実行には torchtransformerssafetensors などが必要なので、uv で仮想環境を作ってから使います。

uv venv .venv && source .venv/bin/activate && \
  uv pip install torch safetensors huggingface_hub numpy transformers
source .venv/bin/activate && python3 tools/make_glm_oracle.py
prompt: [3, 14, 159, 26, 53, 58, 200, 11, 77, 240, 5, 99]
full  : [3, 14, 159, 26, 53, 58, 200, 11, 77, 240, 5, 99, 207, 187, 119, 103, ...]
tf_pred: [139, 197, 123, 34, 197, 34, 197, 197, 197, 193, 193, 207, 187, 119, ...]

salvato: glm_tiny/ (pesi+config) e ref_glm.json

hidden 128・5 層・エキスパート 8 個/層という小さな glm_tiny モデルと、その正解出力 ref_glm.json が生成されました。ここで自作の C エンジンを teacher-forcing モードで走らせ、オラクルと突き合わせます。

SNAP=./glm_tiny TF=1 ./glm 64 16 16
[DSA] indexer attivo: attenzione sparsa top-4096 oltre 4096 token di contesto
[MTP] assente (draft=0)
[RAM_GB=21.4 auto] cap=64 ok (proiezione picco 3.7 GB)
[prefill] layer 1/5 · 32 token
[prefill] layer 5/5 · 32 token
== Motore C GLM (glm_moe_dsa), cache=64 expert/layer | expert@16-bit densa@16-bit | idot: neon ==
caricato in 0.00s | densa residente: 1.57 MB | layers=5 experts=8 | MTP assente (draft=0)
PREFILL (teacher-forcing) C vs oracolo: 32/32 posizioni | 708.1 pos/s
PROFILO: expert-disk 0.000s | expert-matmul 0.001s | attention 0.038s (di cui kvb 0.000s) | lm_head 0.000s | altro 0.005s

32/32 posizioni、つまり全ポジションで C エンジンの出力が transformers オラクルと完全一致していることが確認できました。MLA(圧縮 KV キャッシュ付きアテンション)と DSA(スパースアテンション)、DeepSeek-V3 系のルーターを含むフォワードパス全体が、ゼロから書かれた C コードで正しく再実装されていると言えそうです。ログの idot: neon という表記からも、先ほど見つけた ARM NEON カーネルが実際に使われていることがわかりますね。

ユニットテストと量子化のセルフテストを流す

アーキテクチャの検証に続けて、リポジトリに同梱されているユニットテスト一式も流してみます。

source .venv/bin/activate && make check
json tests: ok
safetensors primitive tests: ok
tier tests: ok
python3 -m unittest discover -s tests -p 'test_*.py'
...
----------------------------------------------------------------------
Ran 27 tests in 1.107s

OK

C 側の JSON パーサや safetensors 読み込み、階層(tier)管理に加えて、openai_server.py の OpenAI 互換 API を実際にローカルでリクエストしてテストする Python テストまで含めて 27 件すべて成功しました。

もう一つ、colibrì の根幹である「FP8 → int4 変換」の量子化ロジックにもセルフテストが用意されています。実モデルをダウンロードしなくても、合成データに対する量子化誤差を検証できます。

source .venv/bin/activate && python3 tools/convert_fp8_to_int4.py --selftest
[selftest fp8 block-dequant] errore relativo medio = 0.0225  (OK)

平均相対誤差 2.25% で許容範囲内という判定です。744B パラメータのモデルを 370GB まで圧縮する変換パイプラインが、少なくとも合成データ上では狙いどおりに動いていることが確認できました。

coli の CLI を触ってみる

ここまでの glm は低レイヤーの検証用バイナリでしたが、実際の利用者はユーザー向け CLI の coli を使います。まず glm_tiny を指すと、safetensors のヘッダーだけからアーキテクチャを正しく読み取ってくれることがわかります。

COLI_MODEL=./glm_tiny ./coli info
   modello    ./glm_tiny
   arch       hidden 128 · 5 layer · 8 expert/layer · top-2
   shard      1 file · 0 GB su disco
   disco      525 GB liberi
   motore     pronto ✓

coli plan も同様に、モデルのヘッダーだけを読んで RAM/ディスクの計画を立ててくれます。

COLI_MODEL=./glm_tiny ./coli plan
  model  1 shards · 0.0 GB
  disk   backing store · 524.7 GB free
  RAM    8.0 GB budget · 0.0 GB dense · 3.7 GB runtime · cap 8/layer
  VRAM   no NVIDIA device detected · CPU path

ところが実際に文章を生成させようとする coli run は、ここで初めてつまずきました。

COLI_MODEL=./glm_tiny ./coli run "hello" --ngen 16
manca tokenizer.json in ./glm_tiny

tokenizer.json が見つからない」というエラーです。考えてみれば当然で、glm_tinymake_glm_oracle.py がテスト専用に作ったトークン ID レベルの検証用フィクスチャであり、実物のトークナイザーは含まれていません。テキストでチャットするには、Hugging Face 上で配布されている本物の GLM-5.2 のトークナイザーと、それに対応するモデルが必要というわけです。

実際にトークンを生成させてみる

とはいえ、本物のモデルなしでも生成パイプライン全体(ディスクストリーミング・LRU キャッシュ・投機的デコード)は動かせます。tools/make_glm_bench_model.py は、CUDA バックエンドの A/B 比較用に用意されたスクリプトで、実アーキテクチャの形状を保ったまま 313M パラメータ程度(hidden 1024・8 層・32 エキスパート)まで縮めたランダム重みモデルを生成します。README にも明記されているとおり「言語モデルとしては無意味だが、MLA/MoE/ストリーミングの形状を保持した比較用フィクスチャ」という位置づけです。

source .venv/bin/activate && \
  python3 tools/make_glm_bench_model.py --output ./glm_bench_medium --device cpu

生成された model.safetensors は 1.2GB でした。これを glm バイナリに読み込ませ、実際に 20 トークン生成させてみます。

SNAP=./glm_bench_medium ./glm 64 4 4
[DSA] indexer attivo: attenzione sparsa top-4096 oltre 4096 token di contesto
[MTP] assente (draft=0)
[RAM_GB=21.7 auto] cap=64 ok (proiezione picco 6.2 GB)
== Motore C GLM (glm_moe_dsa), cache=64 expert/layer | expert@4-bit densa@4-bit | idot: neon ==
caricato in 0.08s | densa residente: 29.46 MB | layers=8 experts=32 | MTP assente (draft=0)

Riferimento (oracolo): 207 187 119 103 103 103 103 103 119 34 103 103 103 103 103 136 112 7 119 34
Motore C GLM         : 2133 1375 111 1375 663 2828 5841 962 6060 2938 4945 3536 494 3866 1384 91 6277 4017 494 7957
Token coincidenti: 0/20
Speculazione n-gram (DRAFT=0): 1.05 token/forward (19 fw per 20 tok)
Hit-rate cache expert: 83.5% (hit=731 miss=144) | RSS: 0.32 GB | 45.1 tok/s
PROFILO: expert-disk 0.206s | expert-matmul 0.132s | attention 0.071s (di cui kvb 0.000s) | lm_head 0.000s | altro 0.035s

「Token coincidenti: 0/20」、つまりオラクルと 1 つも一致していませんが、これはバグではありません。このフィクスチャは int4 まで量子化した状態で読み込まれており(expert@4-bit densa@4-bit)、そもそも重みがランダムなためロジットもほぼ一様で、量子化によるわずかな数値誤差だけで argmax の結果が簡単に反転してしまいます。README 自身もこのフィクスチャの用途を「同じ replay トークンで異なるバックエンドの実行を比較するため」と明記しており、Python オラクルとの正解一致を見るものではありません。正しさの検証は前章の glm_tiny の teacher-forcing テストがすでに 32/32 で担っています。

一方で、この実行から得られる数値はすべて本物です。エキスパートキャッシュのヒット率 83.5%、RSS 0.32GB、45.1 tok/s という数字に加えて、MTP ヘッドがなくても n-gram ベースの投機的デコードが 1 フォワードあたり 1.05 トークンを稼いでいること、ディスク読み込み・行列積・アテンションそれぞれの所要時間まで、実機で実際に動かした結果として得られています。

ディスクの帯域を測ってみる

colibrì はコールドデコード時に 1 トークンあたり約 11GB ものディスク読み込みが発生するため、ディスク帯域が体感速度を大きく左右します。同梱の iobench で、エンジンが実際に使う読み込みパターン(19MB ブロック・8 スレッド並列)を模した計測ができます。ソース冒頭のコメントには gcc -O2 -fopenmp iobench.c -o iobench とありますが、ここでも Apple の clang は -fopenmp を単体では解釈できないため、setup.sh のビルドと同じ要領でフラグを明示的に渡す必要があります。

clang -O2 -Xclang -fopenmp -I/opt/homebrew/opt/libomp/include \
  iobench.c -o iobench -L/opt/homebrew/opt/libomp/lib -lomp
./iobench glm_bench_medium/model.safetensors 19 64 8 0
./iobench glm_bench_medium/model.safetensors 19 64 8 1
buffered x8 thread: 64 letture x 19MB = 1.3 GB in 0.02s -> 53.47 GB/s (0.4 ms/blocco effettivi)
O_DIRECT x8 thread: 64 letture x 19MB = 1.3 GB in 0.02s -> 76.55 GB/s (0.3 ms/blocco effettivi)

本物の 744B モデルをダウンロードして動かす

README の「Download the model」の節には、変換済みの GLM-5.2 int4 モデルが Hugging Face 上で配布されていることが書かれています。coli convert による FP8(756GB)からの変換は README 曰く「時間がかかる(ore = 数時間)」処理ですが、この事前変換済みモデルを直接ダウンロードすればその工程を丸ごと省略できます。せっかく RAM 48GB・NVMe な MacBook Pro が手元にあるので、実際にダウンロードして本物のモデルで生成まで試してみましょう(colibrì 自体の設計目標は RAM 25GB 程度ですが、手元のマシンには余裕があるので RAM 予算は 40GB 前後を指定していきます)。

モデルをダウンロードする

配布リポジトリを確認すると、safetensors シャードが 144 ファイルで合計約 379GB でした。huggingface_hubsnapshot_download でダウンロードします。

python3 -c "
from huggingface_hub import snapshot_download
snapshot_download(repo_id='jlnsrk/GLM-5.2-colibri-int4', local_dir='glm52_i4', max_workers=8)
"
Note

379GB のダウンロードには数十分から数時間かかります。途中でダウンロードプロセスが一度停止しても、snapshot_download はレジューム可能な設計なので、同じコマンドを打ち直すだけで残りを取得できます。

ダウンロードが終わったら coli info で認識させてみます。

COLI_MODEL=./glm52_i4 ./coli info
   modello    ./glm52_i4
   arch       hidden 6144 · 78 layer · 256 expert/layer · top-8
   shard      144 file · 379 GB su disco
   disco      146 GB liberi
   motore     pronto ✓

hidden 6144・78 層・256 エキスパート/層・top-8 ルーティングと、セルフテストで使った glm_tiny とは桁違いの、正真正銘 GLM-5.2 本体のアーキテクチャが表示されました。

リソース計画を確認する

coli plan でリソース計画も確認します。

COLI_MODEL=./glm52_i4 ./coli plan
  model  144 shards · 378.8 GB
  disk   backing store · 145.8 GB free
  RAM    8.0 GB budget · 10.7 GB dense · 6.1 GB runtime · cap 0/layer
  warn   RAM budget cannot hold one expert slot per sparse layer

デフォルトの RAM 予算(8GB)では「スパースレイヤーごとにエキスパートを 1 つもキャッシュできない」という警告が出ました。手元のマシンには 48GB の RAM があるので、--ram で明示的に予算を渡します。

COLI_MODEL=./glm52_i4 ./coli plan --ram 40
  RAM    40.0 GB budget · 10.7 GB dense · 6.1 GB runtime · cap 16/layer

cap が 0 から 16/layer に増え、警告も消えました。

生成してみる

この設定で実際に生成させてみます。

COLI_MODEL=./glm52_i4 ./coli run "colibriについて一文で説明してください。" --ram 40 --ngen 40 --temp 0

ところがこのコマンドは実行するとすぐにこう落ちました。

./glm52_i4/out-00127.safetensors: Too many open files

macOS はプロセスあたりのファイルディスクリプタ数のデフォルト上限が 256(ulimit -n で確認できます)と低く、144 個のシャードをまたいでエキスパートをストリーミングする colibrì には足りないと考えられます。上限を上げてから実行し直します。

ulimit -n 4096
COLI_MODEL=./glm52_i4 ./coli run "colibriについて一文で説明してください。" --ram 40 --ngen 40 --temp 0
[MTP] attiva: decodifica speculativa nativa (draft=3)
[RAM_GB=40.0] cap ALZATO 8->16: il budget lo consente (proiezione picco 39.7 GB; CAP_RAISE=0 per disattivare)
== Motore C GLM (glm_moe_dsa), cache=8 expert/layer | expert@8-bit densa@8-bit | idot: neon ==
caricato in 2.15s | densa residente: 9780.06 MB | layers=78 experts=256 | MTP ATTIVA (draft=3)
prompt: 14 token | genero fino a 40 (stop EOS=154820) | draft n-gram=3
[gMASK]<sop><|user|>colibriについて一文で説明してください。<|assistant|><think></think>[prefill] layer 1/78 · 14 token
...
[prefill] layer 78/78 · 14 token
世界中で約330種類が知られる小[MTP] acceptance 4% dopo 24 proposte: draft disattivati
鳥の総称で
、羽ばたきながら空中に「ホバリング(停止)」
して長い嘴と鋭
---
40 token in 289.49s (0.14 tok/s) | hit-rate expert 35.4% | RSS 18.00 GB
expert caricati/token: 1147.6 (per-layer 15.30 su 75; baseline topk=8) | TOPK=0 TOPP=0.00
speculazione: 1.05 token/forward (38 fw per 40 tok) | MTP acceptance 4% (1/24)
PROFILO: expert-disk 94.641s | expert-matmul 92.048s | attention 70.775s (di cui kvb 0.218s) | lm_head 1.322s | altro 30.700s
Note

ログの cache=8 expert/layer | expert@8-bit densa@8-bit という表示は少し紛らわしい箇所です。直前の行で cap ALZATO 8->16(RAM 予算に応じて 8 から 16 へ引き上げ。この自動引き上げ自体は Issue #12 を受けて 2026-07-10 に追加された挙動です)と出ているのに、バナー上の cache=8 は引き上げ前の値のままになっています。ソースを追うと coliglm バイナリをキャッシュサイズ以外の引数を渡さずに呼び出しており、cache=8-bit はいずれもソース上の未指定時デフォルト・初期値がそのまま表示されているだけで、実際にキャッシュ上限として使われるのは RAM 予算から自動計算された値(この場合 16)です。ディスク上の量子化も int4 のままで、密な部分の常駐サイズが 9780MB と README の見積もり(~9.9GB)にほぼ一致することからも確認できます。

生成されたトークンをつなげると次のようになりました(40 トークンで打ち切り)。

世界中で約330種類が知られる小鳥の総称で、羽ばたきながら空中に「ホバリング(停止)」して長い嘴と鋭…

「colibrì について一文で」と尋ねただけで、colibrì がイタリア語でハチドリを意味することを踏まえた、事実として正しい説明が生成されていることがわかります。int4 まで圧縮されディスクからストリーミングされる 744B パラメータのモデルでも、実用的な応答を組み立てられることが確認できました。

Note

ログにはもう一つ見逃せない行があります。「MTP acceptance 4% dopo 24 proposte: draft disattivati」、つまり MTP の投機的デコードが 24 回の提案で 4% しか採用されず、エンジンが自動的に draft を無効化しています。今回ダウンロードした jlnsrk/GLM-5.2-colibri-int4 の MTP ヘッドは int4 のままで、README がまさに警告していた「int4 ヘッドでは draft acceptance が 0〜4% まで落ち込む」という状態そのものです。expert caricati/token: 1147.6 という数字も、README の「コールドキャッシュでは検証された draft がさらに多くのエキスパートを呼び、1 トークンあたり ~660 → ~1100 expert-loads に増える」という記述とほぼ一致しています。 README では、この acceptance の落ち込みを解消する手段として int8 の MTP ヘッドへの差し替えが挙げられています(コミュニティ版が mateogrgic/GLM-5.2-colibri-int4-with-int8-mtp として配布されています)。ただし、それで実際に速くなるのかというと話はそう単純ではありません。Issue #8 には、今回とよく似た環境(macOS・Apple Silicon・128GB 統合メモリ・SSD 実測 ~18GB/s)での詳細な検証が投稿されています。それによると、MTP ヘッドを int8 化して acceptance を 4% から 39〜59% まで改善させても、投機的デコードはコールド・ウォームいずれのキャッシュでも 23〜28% のネットスローダウンになったと報告されています。理由は、検証される draft トークンがそれぞれ追加のエキスパートを呼ぶため、フォワード回数が減っても 1 フォワードあたりのエキスパート読み込みや行列積がかえって増えてしまうからです。acceptance が健全でも、この構成では MTP 自体が純粋な足かせになるという、README の書きぶりより一段踏み込んだ知見が Issue には残されています。

最終的なスループットは 40 トークンを 289.49 秒、0.14 tok/s でした。README の開発者環境(WSL2、コールドで 0.05〜0.1 tok/s)よりは速いものの、同じ Apple Silicon の M5 Max の計測値(1.06 tok/s、ただし MTP オフ)には届きません。上記の Issue #8 を踏まえると、この遅さは MTP ヘッドの精度をいくら上げても解消しない可能性が高く、cap や expert-loads の増加も踏まえると、むしろ DRAFT=0 で MTP 自体をオフにした方が速くなるかもしれません(この記事では未検証です)。

いずれにせよ、GLM-5.2が本来得意なコーディング領域での活用は厳しい数値であることは間違いありませんが、無料で高性能なLLMがローカルでも動かせるというのは特筆すべき事実です。

学習キャッシュの効果を確認する

colibrì には「使うほど速くなる」学習キャッシュという機能もあるので、同じプロンプトをもう一度実行してみました。

COLI_MODEL=./glm52_i4 ./coli run "colibriについて一文で説明してください。" --ram 40 --ngen 40 --temp 0
[USAGE] storia expert: 45904 selezioni (./glm52_i4/.coli_usage)
[PIN] hot-store: 143 expert in RAM (2.7 GB) in 0s da ./glm52_i4/.coli_usage
[PIN] mlock: 2.7 GB inchiodati in RAM fisica / wired in physical RAM (niente compressione/no compression) in 0s
...
40 token in 253.65s (0.16 tok/s) | hit-rate expert 36.9% | RSS 19.87 GB

前回の実行で記録された .coli_usage(エキスパートの利用履歴)から、143 個のホットなエキスパート(2.7GB)を自動的に RAM へピン留めしていることがログからわかります。生成されたトークン列は 1 回目とまったく同じ(温度 0 の貪欲デコードなので当然です)でしたが、所要時間は 289.49 秒 → 253.65 秒、エキスパートキャッシュのヒット率は 35.4% → 36.9% と、わずかながら実際に速くなりました。README が謳う「使うほど速くなる」という性質を、自分の手元でも確認できたことになります。

まとめ

  • colibrì は GLM-5.2(744B MoE)を、密な部分だけ RAM に常駐させルーティング対象エキスパートをディスクからストリーミングすることで、25GB 前後の RAM で動かす C 製推論エンジンである
  • tools/make_glm_oracle.py で生成した tiny フィクスチャに対する teacher-forcing テストは 32/32 で完全一致し、MLA・DSA・MoE を含むフォワードパスの再実装が正しいことを自分の手元でも確認できた
  • 313M パラメータのランダム重みフィクスチャでも生成パイプライン全体は動作し、エキスパートキャッシュのヒット率や tok/s、n-gram 投機的デコードの効果など、実機の生の数値を得ることができた
  • Hugging Face 上の事前変換済み int4 モデル(約 379GB)をダウンロードし、本物の GLM-5.2 に coli run でプロンプトを投げて実際に応答を生成できた(0.14 tok/s、colibrì について事実として正しい説明が返ってきた)
  • 現状さらに高速化することは難しく、コーディングでの利用は難しいが、高性能なLLMを簡単にローカルでも動かせるという事実は大きい

参考