RAM 32GBのMacBookとワークステーションで744B MoEモデルを停止させずに動作させる設定
一般のワークステーションやMacBookでGLM-5.2のような744B MoEモデルをローカルで駆動する場合、ボトルネックはVRAM容量よりもストレージの帯域幅とOSの仮想メモリポリシーで先に発生します。C言語ベースの軽量推論エンジンであるJustVuggのcolibrìは、17Bサイズの常駐レイヤー(int4基準9.9 GiB)のみをRAMに固定し、21,504個に分割したルーティング専門家ウェイト(約370 GB)を外付けSSDからトークン単位でリアルタイムストリーミングします。
問題は、OSのデフォルト設定のまま実行するとスワップスラッシングが発生したり、カーネルのOOM Killerがプロセスを強制終了したりする点です。コンパイルフラグからNVMe I/Oキュー、カーネルパラメータまで直接手を加えることで、トークン生成が停止しなくなります。
アーキテクチャ別コンパイルフラグとベクトル命令セットの設定
colibrìのコア演算エンジンは、外部依存性のない約1,300行分の単一のC11ファイルです。コンパイラがどのSIMDベクトル命令を使用するかによって、行列積演算の処理量が分かれます。デフォルトの-O2ビルドの代わりに、ターゲットCPUの整数演算拡張命令を直接指定する必要があります。
ビルド環境の構築とコンパイル
x86_64 Linux環境ではGCC 12以上でAVX-512 VNNIを有効にし、Apple SiliconではClangを通じてNEON DotProductを結合します。
UbuntuおよびDebian系では、ビルドツールを事前にインストールします。
`bash
sudo apt-get install gcc-12 libomp-dev
`
macOS環境ではHomebrewでOpenMPランタイムを取得します。
`bash
brew install libomp
`
x86_64 Linuxマシンでは、AVX-512 VNNI命令をターゲットにしてビルドします。
`bash
gcc -O3 -march=native -mtune=native
-mavx512f -mavx512bw -mavx512vnni -mavx512dq
-fopenmp -funroll-loops -ffast-math
-o colibri_engine colibri_glm52.c -lm
`
Apple Silicon(Mシリーズ)環境では、ライブラリパスを直接指定してコンパイルします。
`bash
clang -O3 -mcpu=native
-march=armv8.4-a+dotprod+fp16
-Xpreprocessor -fopenmp
-I/opt/homebrew/opt/libomp/include
-L/opt/homebrew/opt/libomp/lib -lomp
-o colibri_engine colibri_glm52.c -lm
`
構造体に関するビルド警告が表示される場合は、-std=c11フラグを付与して解決します。
| プラットフォーム |
コンパイラ |
ベクトル命令フラグ |
スレッド並列化 |
| x86_64 (Linux) |
GCC 12+ |
-mavx512vnni -mavx512bw |
-fopenmp |
| ARM64 (macOS) |
Apple Clang |
-march=armv8.4-a+dotprod |
-Xpreprocessor -fopenmp |
| 旧型x86 |
GCC / Clang |
-mavx2 -mfma |
-fopenmp |
外付けSSDのベンチマークと非同期I/Oキューの拡張
colibrìのデコード遅延時間は、ディスクの1MB単位のランダム読み取り速度が決定します。GLM-5.2のルーティング専門家パラメータファイルは1つあたり約19 MBサイズに分かれており、トークンを生成するたびに非同期I/OスレッドがSSDから必要なチャンクを読み込みます。
fioを通じたストレージ読み取り帯域幅の測定
パッケージマネージャーでfioをインストールします。
`bash
Linux
sudo apt-get install fio
macOS
brew install fio
`
1MBのブロックサイズとダイレクトI/O(Direct I/O)の条件で、ランダム読み取り帯域幅を測定します。
`bash
fio --name=colibri_stream_bench
--filename=/Volumes/ExternalSSD/glm52_test.tmp
--size=10G
--rw=randread
--bs=1m
--iodepth=32
--numjobs=4
--ioengine=posixaio
--direct=1
--group_reporting
--runtime=60
`
Thunderbolt 4やUSB4(40 Gbps)インターフェースは、実測帯域幅2,800〜3,200 MB/sを維持し、0.08〜0.10 tok/sのデコード速度を発揮します。一方、帯域幅が900〜1,050 MB/sにとどまるUSB 3.2 Gen2(10 Gbps)ポートに外付けドライブを接続すると、100トークンを生成するのに80分以上かかります。
`
+-------------------------------------------------------------------------+
| colibrì C Inference Engine |
| +--------------------------+ +-------------------------------------+ |
| | Dense Weights (9.9 GiB) | | Multi-Token Prediction (MTP) Head | |
| | Resident in RAM (mlock) | | int8 Quantized | |
| +------------+-------------+ +------------------+------------------+ |
+---------------+-----------------------------------+---------------------+
| |
v v
+-------------------------------------------------------------------------+
| Memory & Storage I/O Layer |
| +--------------------------+ +-------------------------------------+ |
| | Async Expert Readahead | | 21,504 Routed Experts (370 GB) | |
| | I/O Queue Depth 3264 | | Streamed from NVMe SSD via mmap | |
| +--------------------------+ +-------------------------------------+ |
+-------------------------------------------------------------------------+
`
非同期プリフェッチ(Async Expert Readahead)のI/Oキュー深度(iodepth)を32〜64水準に増やすことで、NVMeコントローラーの並列チャネルをすべて活用し、ディスクの待ち遅延を減らすことができます。
カーネル仮想メモリおよびOOM防止設定
32 GB RAMシステムで9.9 GiBの常駐ウェイトとKVキャッシュ、ファイルシステムページキャッシュが同時に読み込まれると、カーネルメモリの競合が発生します。デフォルトのLinux設定では、370 GBサイズのmmap呼び出し時に仮想アドレス空間の割り当てがブロックされるか、OOM Killerがエンジンを終了させてしまいます。
Linuxカーネルパラメータの適用
/etc/sysctl.d/99-colibri-memory.confファイルを作成し、以下の内容を入力します。
`ini
常駐メモリのディスクスワップ放出の抑制
vm.swappiness = 1
仮想メモリのオーバーコミットを許可
vm.overcommit_memory = 1
非常用バッファメモリ空間の確保 (2GB)
vm.min_free_kbytes = 2097152
ディスクファイルキャッシュの保持率を上方修正
vm.vfs_cache_pressure = 50
mmapマッピング数の制限を拡張
vm.max_map_count = 524288
`
保存後、パラメータを即時反映します。
`bash
sudo sysctl --system
`
シェルセッションでメモリロック制限を解除し、常駐レイヤーのmlock()呼び出しを許可します。
`bash
ulimit -l unlimited
`
| パラメータ |
デフォルト値 |
推奨値 |
設定目的 |
| vm.swappiness |
60 |
1 |
RAMに固定した9.9 GiBのウェイトがスワップ領域に押し出される現象の防止 |
| vm.overcommit_memory |
0 |
1 |
370 GB分のmmapマッピング時のカーネルメモリ拒否の防止 |
| vm.vfs_cache_pressure |
100 |
50 |
ファイルキャッシュの寿命を延ばし、繰り返し呼び出される専門家ウェイトを再利用 |
| vm.max_map_count |
65530 |
524288 |
21,504個のパラメータファイルのマッピング制限解除 |
| ulimit -l |
64 (KB) |
unlimited |
Denseレイヤーの物理メモリ固定(mlock)権限の付与 |
macOS環境では、空きメモリが枯渇するとdynamic_pagerがバックグラウンドでメモリ圧縮を実行し、CPU占有率を圧迫します。駆動前に占有率の高いアプリを整理し、圧縮エンジンの稼働を防ぐ必要があります。
CPUコアピニングと実行スクリプトの構成
MoEデコードは、CPUキャッシュとメモリバスを休むことなく消費します。マルチソケットワークステーションでNUMAノードを誤って割り当てると、インターコネクトバスのボトルネックによりトークン生成速度が半分に低下します。
統合駆動スクリプトの作成
numactl --hardwareで外付けNVMeコントローラーと直結された物理CPUのノード番号を事前に確認します。その後、ハイパースレッディングの重複割り当てを避け、物理コアにのみスレッドを固定するシェルスクリプトを作成します。
`bash
#!/usr/bin/env bash
set -euo pipefail
1. カーネル仮想メモリの設定
sudo sysctl -w vm.swappiness=1 > /dev/null
sudo sysctl -w vm.overcommit_memory=1 > /dev/null
sudo sysctl -w vm.max_map_count=524288 > /dev/null
sudo sysctl -w vm.vfs_cache_pressure=50 > /dev/null
sudo sync && echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null
2. メモリロック制限の解除
ulimit -l unlimited
3. OpenMPスレッドの物理コアバインディング
export OMP_NUM_THREADS=16
export OMP_PROC_BIND=TRUE
export OMP_PLACES=cores
4. NUMAノード0へのバインディング実行
ENGINE_BIN="./colibri_engine"
MODEL_PATH="/mnt/nvme_ext/GLM-5.2-colibri-int4-g64-with-int8-mtp"
exec numactl --physcpubind=0-15 --membind=0
"${ENGINE_BIN}"
--model "${MODEL_PATH}"
--threads "${OMP_NUM_THREADS}"
`
Apple Silicon MacBookでは、macOSのQoSデーモンがワーカーのスレッドを効率コア(Eコア)に移動させないよう対策する必要があります。優先度を強制的につり上げて実行します。
`bash
sudo nice -n -20 taskpolicy -c default ./colibri_engine --model /Volumes/SSD/glm52_i4
`
これら4つの設定を完了させることで、32 GB RAMのワークステーションやMacBookでもOOMクラッシュなしに、744B MoEモデルをローカルディスクストリーミング方式で安定して稼働させることができます。