ElcamyTECH
Articles
Ollama

llmfit の tok/s 推定は当たるのか — 構成を指定すれば±20%以内、既定のままだと−36%

TechOllamaOllamaローカルLLMMacベンチマーク2026/09/22

llmfit は、ハードを検出して「このマシンでどのローカルLLMが動くか」「何 tok/s 出るか」を推定してくれるツールです。★3.5万・MIT。その推定は当たるのかを、M4 MacBook Air 16GB で実測と突き合わせました。

合格ラインは測る前に決めました。**推定と実測の差が ±20% 以内なら「当たり」**とします。

問い答え根拠
速度(tok/s)を推定できたかできた。ただし構成を指定したときだけplan に量子化とコンテキストを渡した推定は、実測の 85〜115%。5回の突き合わせのうち4回が±20%内
何も指定しない既定の値は当たるか当たらない一覧(fit)の値は実測の 55〜75%。原因は精度ではなく前提(mlx-8bit で計算している)
メモリ使用量を推定できたかだいたい当たった推定÷実測が 106%・122%・128%。3件とも多めに見積もる安全側
普段使いでメモリが埋まった日も当たるか当たらない同じMac・同じモデルの実測が 5.7〜36.3 tok/s(6倍差)。推定は1つの値のまま

まとめると、買う前・入れる前に当たりを付ける道具としては使えます。ただし量子化とコンテキストを自分で指定して読むこと。速度の絶対値は「メモリに余裕がある日の値」として受け取るのが正しいです。

測った環境

項目
マシンMacBook Air(Apple M4・10コア・16GB ユニファイドメモリ)・macOS 26.5
llmfit1.1.15(GitHub Releases の aarch64-apple-darwin バイナリ・sha256 検証済み)。比較用に 1.1.14 も
Ollama0.33.3(公式の ollama-darwin.tgz
MLXmlx-lm 0.31.3 / mlx 0.32.2(arm64 の Python 3.12)
モデルgemma3:4b(Q4_K_M・4.3B・ディスク3.11GiB)、qwen3.5:4b(Q4_K_M・4.7B・3.16GiB)、mlx-community/gemma-3-4b-it-4bit
状態普段どおりアプリを開いたまま(Slack・Chrome・Notion・エディタ等)。空きメモリとスワップは1回ごとに記録

計測はあえて普段使いのままにしています。 「このMacで動くのか」を知りたいのは普段の状態で使うからで、アプリを閉じて空きメモリを作った数字は自分の役に立ちません。代わりに、そのときの空きメモリ%とスワップ使用量を一緒に載せます。

測り方

llmfit 側は2通りの推定を取りました。同じツールでも出てくる数字が違います。

bash
# ① 何も指定しない既定の一覧(8,410モデルを並べる)
llmfit fit --json --no-dashboard
 
# ② 実際に使っている構成を指定する(量子化とコンテキスト長)
llmfit plan "google/gemma-3-4b-it" --context 4096 --quant Q4_K_M --json

実測は2つのランタイムで揃えました。プロンプトは同じ日本語の指示文、最大200トークン、greedy(温度0)、seed=42、5回まわして中央値です。

  • Ollama: /api/generatenum_ctx=4096 で投げ、eval_count ÷ eval_duration(生成部分だけ)を tok/s とする。モデル間は keep_alive:0 でアンロード
  • MLX: mlx-lm の stream_generate で、チャットテンプレートを通した同じプロンプトを流し、generation_tpspeak_memory を採る

9月6日の計測だけ3回の中央値です。それ以外は5回です。

結果1: 構成を指定した推定は当たった

llmfit の推定を実測で割った値。構成を指定した5件は85〜115%で±20%の帯の中、既定の一覧は55〜75%で帯の外

実測した構成②構成を指定した推定実測(中央値)推定÷実測判定
gemma3:4b Ollama Q4(9月6日)30.7 tok/s31.3 tok/s98%当たり
gemma3:4b Ollama Q4(9月12日・MLX の前)30.7 tok/s26.8 tok/s115%当たり
gemma3:4b Ollama Q4(9月12日・MLX の後)30.7 tok/s27.8 tok/s110%当たり
gemma3:4b MLX 4bit(9月12日)30.7 tok/s36.3 tok/s85%当たり
qwen3.5:4b Ollama Q4(9月6日)28.3 tok/s24.7 tok/s115%当たり

ランタイムを Ollama から MLX に変えても帯の中に収まりました。llmfit は Q4_K_M と mlx-4bit に同じ 30.7 tok/s を返すので、4bit なら実装の違いは推定の誤差の内側、という結果です。

plan はメモリ量も返します。こちらも近い値でした。

実測した構成推定メモリ(KVキャッシュ込み)実測推定÷実測
gemma3:4b Ollama Q43.53GB(うちKV 0.53GB)2.89GB(ollama ps の占有)122%
qwen3.5:4b Ollama Q43.33GB(うちKV 0.13GB)3.15GB(同)106%
gemma3:4b MLX 4bit3.40GB2.65GB(mlx のピーク)128%

3件とも多めに見積もる方向です。「載るか」の判断に使うなら、この向きの誤差は安全側です。

結果2: 既定の一覧が外れるのは前提の違い

一覧(fit)の値は gemma3:4b が 20.1、qwen3.5:4b が 18.5 でした。実測の 55〜75% で、帯の外です。ただし中身を見ると、外れているのは式ではなく前提です。

llmfit は「メモリに余裕があるなら、いちばん品質の高い量子化で動かすべき」という設計で、この Mac には mlx-8bit を選びます。私が入れているのは Q4_K_M と mlx-4bit。別の構成の数字を見ていたわけです。同じ plan で量子化を変えるとこうなります。

指定した量子化gemma3:4bqwen3.5:4b
Q4_K_M(Ollama の既定タグ)30.728.3
mlx-4bit30.728.3
mlx-8bit(llmfit が勧める構成)15.314.2

Ollama を起動していても変わりません。installed: true と検出はされるのに、best_quantmlx-8bit のままです。「あなたが入れているモデルの速度」ではなく「このモデルをこのハードで動かすなら、こう動かすべき」の推定だと読むのが正しいです。

もう1つ、同じモデルでコマンドによって数字が変わる件も残っています。fit は 20.1、plan --quant mlx-8bit は 15.3。比は 1.31 で、これは fit が掛けているローカル較正係数(起動時に測った実効帯域から出る 1.309)と一致します。Web ダッシュボードは較正なしの側で、CLI の fit が 11.7 と出すモデルが画面では 8.9 と出ます。比べるときは1つのコマンドに揃えてください。

llmfit の Web ダッシュボード。上部にハード検出(Apple M4・16GB)、下にモデル一覧と TPS 列

結果3: 同じモデルが、日によって6倍振れる

普段使いのまま、同じコマンドで測った gemma3:4b の実測を並べます。

同じ gemma3:4b の実測。MLX 4bit 36.3、9月6日 31.3、9月12日 26.8、9月11日 5.7 tok/s。点線は構成指定の推定 30.7

計測空きメモリスワップ使用量実測(中央値)ばらつき
9月12日 MLX 4bit31〜35%約15GB36.3 tok/s31.9〜37.5
9月6日 Ollama Q4llmfit 表示で 1.3〜2.4GB未記録31.3 tok/s31.2〜33.7
9月12日 Ollama Q421〜44%13〜16GB26.8 tok/s22.1〜27.9
9月11日 Ollama Q419〜21%15→19GB5.7 tok/s3.8〜8.9

最も遅い日と最も速い計測で6倍の差です。9月11日もモデルは GPU に全部載っていて(ollama ps の VRAM 表示は 2.69GiB / 2.69GiB)、初回ロードに 16〜58秒かかり、5回の中でも倍以上ばらつきました。スワップが19GB積まれた状態では重みがメモリに居続けられず、読み直しが起きていると考えられます(ここは推測で、ページフォールト数までは測っていません)。

llmfit の推定は、この「その瞬間のメモリの混み具合」を速度に反映しません。plan の 30.7 tok/s は、スワップ13〜16GBの日には 110〜115% で当たり、19GBまで積んだ日には 5.4倍の過大評価になります。**推定値は「メモリに余裕がある日の値」**として読むのが実際に合います。

つまずいたところ

  • Homebrew がソースビルドを始めた。 このMacの Homebrew は /usr/local にある Intel 版で、macOS 26.5 用の bottle が無く、brew install llmfitbrew install ollama も Go と CMake のビルドに入りました。10分以上終わらないので打ち切り、GitHub Releases のバイナリに切り替えています。Apple Silicon 版(/opt/homebrew)の人には起きない話です
  • PyPI 版を入れると x86_64 バイナリが降ってくることがあります。 原因は PyPI ではなく、このMacの pyenv の Python 自体が x86_64 だったことです(python3 -c "import platform;print(platform.machine())" で分かります)。今回 arm64 のネイティブバイナリでも測り直しましたが、推定値も較正係数も完全に同じでした(20.1 / 18.5 / 1.309)。Rosetta 経由かどうかは推定に影響しません
  • 前回の bench の結果が残って、一覧の数字を上書きしていました。 ~/Library/Application Support/llmfit/benchmarks/pending/ に送信待ちのファイルが2件残り、estimate_confidencemeasured_local に変わって「この機体の実測」として表示されます。退避したら calibrated に戻りました
  • 付属の bench は Ollama 自身の計測と定義が違います。 qwen3.5:4b で 13.9 tok/s(同じサーバーへの自前計測は 24.7)。プロンプト処理を含む所要時間で割っているように見えます。その値がそのまま measured_local として保存されるので、回す前に eval_count ÷ eval_duration と一度並べるほうが安全です
  • Ollama の占有が計測の回によって 2.69GiB と 3.47GiB に変わりました。 同じモデル・同じリクエストで、理由は追えていません。メモリ推定との比較には小さいほうの値を使っています
  • ディスクが足りませんでした。 空きが 5.7GB しかなく、13GB ある gpt-oss:20b の追加検証は諦めています

再現手順

bash
# 1. llmfit(ネイティブ arm64 バイナリ。sha256 を検証する)
mkdir -p ~/sandbox/llmfit && cd ~/sandbox/llmfit
gh release download v1.1.15 -R AlexsJones/llmfit --pattern 'llmfit-v1.1.15-aarch64-apple-darwin.tar.gz*'
shasum -a 256 -c llmfit-v1.1.15-aarch64-apple-darwin.tar.gz.sha256
tar xzf llmfit-v1.1.15-aarch64-apple-darwin.tar.gz
L=$PWD/llmfit-v1.1.15-aarch64-apple-darwin/llmfit; $L --version
 
# 2. 既定の一覧と、構成を指定した推定の両方を取る(数字が違う)
$L fit --json --no-dashboard > fit.json
$L plan "google/gemma-3-4b-it" --context 4096 --quant Q4_K_M --json > plan.json
python3 -c "import json;d=json.load(open('plan.json'));p=[x for x in d['run_paths'] if x['path']=='gpu'][0];print(p['estimated_tps'], p['minimum']['vram_gb'])"
 
# 3. Ollama(公式バイナリ。モデルも作業ディレクトリに置く)
gh release download -R ollama/ollama --pattern 'ollama-darwin.tgz' && tar xzf ollama-darwin.tgz
OLLAMA_MODELS=$PWD/models ./ollama serve &
./ollama pull gemma3:4b
 
# 4. 実測(生成部分だけの tok/s。5回まわして中央値)
for i in 1 2 3 4 5; do
  curl -s http://localhost:11434/api/generate -d '{"model":"gemma3:4b","prompt":"製造業の中小企業がローカルLLMを導入する利点と注意点を、日本語の箇条書きで5つ、簡潔に説明してください。","stream":false,"options":{"num_predict":200,"seed":42,"num_ctx":4096}}' > run-$i.json
done
jq -s 'map(.eval_count / (.eval_duration/1e9)) | sort | .[2]' run-*.json
 
# 5. そのときのメモリ状況も一緒に残す(これが無いと後から読めない)
memory_pressure | tail -1; sysctl -n vm.swapusage; ./ollama ps

MLX 側は arm64 の Python で uv pip install mlx-lm し、stream_generategeneration_tps を採りました(pyenv の Python が x86_64 だと mlx が入りません)。

測っていないこと

  • mlx-8bit の実測。 llmfit が勧める構成そのものでは測っていません。今回の「当たった」は 4bit 系(Q4_K_M と mlx-4bit)を指定したときの話です
  • qwen3.5:4b の MLX 実測。 ディスクの都合で gemma だけです
  • 12B 以上のモデル。 「載るか」の判定が効くのは境界付近ですが、ディスクの空きが足りず 13GB 級を落とせませんでした。今回の3GB級はどちらも余裕のある側(Perfect 判定)です
  • メモリが埋まると遅くなる仕組み。 スワップ19GBという状態と6倍の低下は測りましたが、ページフォールト数やディスク読み出し量までは見ていません
  • CPU のみで動かしたとき。 plan は CPU 経路の推定(7.2 / 6.7 tok/s)も返しますが、Apple Silicon では GPU で動くので比較していません

関連記事

Solution

AIエージェント・Dify構築支援

AIエージェント開発・Dify構築・PoC・社内研修まで
ワンストップで支援。まずはお気軽にご相談ください。

Elcamy

Technology Partners

DifyGoogle Cloud Partner