はじめに
ローカルLLMを始めるとき、最初に触るのはたいてい Ollama だと思います。
ollama run qwen3.5:4b
これだけでモデルが落ちてきて、すぐチャットできる。GPUの設定も、量子化の知識も、サーバの起動オプションもいりません。「とりあえずローカルでLLMを動かす」には、Ollamaは本当によくできています。
ただ、使い込んでいくと、こういう瞬間が来ます。
- RAG(社内資料を読ませるやつ)を作ったら、最初の返事までがやけに遅い
- 自分以外の人にも使わせたら、2人目が繋いだ途端に全体が重くなった
- 長いPDFを貼ったら、途中の内容を忘れている気がする
- 速くしたいけど、どこをいじればいいのか分からない
このとき、「Ollamaが遅い」と片付けるのではなく、「どこが遅いのか」を見られるようにするのが、この記事のゴールです。そして、その"どこ"によっては、vLLM や SGLang という選択肢が効いてきます。
結論を先に言うと:
- 1人で・短い会話なら、Ollamaで十分。むしろ一番軽くて速いくらいです。
- でも 複数人・並列リクエストになると、Ollamaには(今回のモデルでは)構造的な壁があります。ここが乗り換えの一番の分岐点。
- RAGや長文も、Ollamaで"できる"。ただし詰めると差が出ます。
RTX 4070 (12GB) で Qwen3.5-4B を動かし、Ollama / vLLM / SGLang を同じ条件で実測した結果をもとに、「Ollamaで始めた人が、どの壁に当たったら次を見るべきか」をお手伝いします
Ollamaのいいところ(まず正直に)
Ollamaを下げる記事ではないので、良い点をちゃんと挙げます。今回 RTX 4070 で測ったら、1人で使う分にはOllamaが最速・最軽量でした。
| 指標(1リクエスト, 4B) | Ollama (既定 Q4) | vLLM (FP8) | SGLang (FP8) |
|---|---|---|---|
| 生成速度 tok/s | 91 | 53 | 67 |
| 最初の1tokenまで (TTFT) | 約0.3秒 | — | — |
| ピークVRAM | 6.1GB | 10.6GB | 11.3GB |
※ Ollamaの既定モデル
qwen3.5:4bは4bit量子化(Q4_K_M)。精度をFP8とそろえたQ8_0でも約68 tok/s で、SGLangと互角でした(後述)。
1人で短文チャットする、モデルをいくつか試す、ちょっとしたAPIアプリを作る——この範囲なら、導入の簡単さも含めてOllamaが快適です。ここで無理にサーバへ行く必要はありません。
Q4vsFP8?
Ollamaのいいところを書く記事のインパクトのために比量子化サイズの較はずるしてる
これに引っかかるあなたはLLMに詳しいのね。
数字は嘘をつかないが、数字を使う人は嘘をつく
Ollamaは「隠してくれる」道具
Ollamaが簡単なのは、面倒なものを全部自動で隠してくれるからです。
- どの量子化を使うか(既定は4bit)
- contextをどれだけ確保するか
- 複数リクエストをどう捌くか
- KVキャッシュをどう持つか
便利な反面、これは「遅いと感じても、どこが遅いのかを見る計器が無い」ことを意味します。tok/s(生成速度)は見えても、体感を左右する別の指標は隠れています。
ここから先は、Ollamaで起きる"症状"→その正体(指標)→見るべきエンジンの順で、3つの壁を見ていきます。
壁①:複数リクエストの壁(ここが一番大きい)
症状:「自分1人なら快適だったのに、2人目が繋いだ瞬間に全体が重くなった」「バックエンドから並列でリクエストを投げたら詰まった」
これがOllamaで一番はっきり出る壁です。そして今回、原因がログにそのまま出ていました。
OLLAMA_NUM_PARALLEL=8(8並列を許可)を指定してOllamaを起動しても、サーバログにこう出ます:
msg="model architecture does not currently support parallel requests" architecture=qwen35
load request="{... Parallel:1 ...}"
つまり Ollama v0.24.0 の実装上、qwen35 / qwen35moe 系は num_parallel > 1 が安全でない扱いになっており、Ollama 側で Parallel:1(直列)に強制されます。リクエストが並ぶと、後ろの人は前の人が終わるまで待つだけ。
これは設定ミスではなく、Ollama v0.24.0 側の実装判断です(qwen35 系は num_parallel > 1 を安全でないとして弾く)。OLLAMA_NUM_PARALLEL をいくら上げても、量子化(Q4/Q8)を変えても Parallel:1 のまま。今のOllamaでQwen3.5を複数同時に捌くのは難しい、という話です(バージョン依存なので、将来の実装で変わる可能性はあります)。
自分の環境で確かめる(並列を上げて serve ログを見る)
# 8並列を許可して起動
OLLAMA_FLASH_ATTENTION=1 OLLAMA_NUM_PARALLEL=8 ollama serve
# 別ターミナルでモデルをロードさせ、serve のログを見る
# -> "does not currently support parallel requests architecture=qwen35"
# "load request=... Parallel:1 ..." が出れば直列化されている
同じ条件(8並列、各2k tokenの入力)で測るとこうなります:
| 指標(8並列) | Ollama | vLLM (FP8) | SGLang (FP8) |
|---|---|---|---|
| スループット合計 tok/s | 66 | 278 (4.2x) | 468 (7.1x) |
| p95 TTFT(最初の1tokenまで・95%タイル) | 27.6秒 | 2.3秒 | 0.28秒 |
| p95 リクエスト全体 | 31秒 | 7.4秒 | 4.4秒 |
8人同時で、Ollamaは最初の応答までp95で27秒。vLLMは2.3秒、SGLangは0.28秒——SGLangはOllamaの約100倍速い。スループット合計もOllamaは1人のときと変わらない(直列だから増えない)のに対し、vLLMは4倍、SGLangは7倍に伸びます。
重要なのは、これは量子化の問題ではないこと。既定のQ4でも、精度を上げたQ8でも、Parallel:1の壁は同じです(Q4でもp95 TTFTは20秒台)。つまりOllama側でモデルを変えても解けない、構造的な壁です。
→ 複数人で使う/並列にリクエストを投げる用途に入ったら、vLLM や SGLang を見るべきです。
vLLM や SGLang は複数リクエストをまとめて処理(バッチング)するので、並列で初めて本領を発揮します。
特に並列時の待ち時間(TTFT/p95)はSGLangが際立って強い(このRTX 4070 + Qwen3.5の条件では)。
壁②:RAG・同じ資料に何度も質問する壁
症状:「同じ社内文書を読ませて何度も質問しているのに、毎回最初の返事が遅い」
長い文書(prefix)を毎回読み直すと、その分の前処理(prefill)に時間がかかります。ここで効くのが prefix の再利用(vLLMの prefix caching / SGLangの RadixAttention)。2回目以降、共通部分の処理を飛ばせます。
うれしいことに、Ollamaにもプロンプトキャッシュがあり、ちゃんと効きます。同じ8k文書に続けて質問すると:
| prefix ~8k | 2回目TTFTの削減率 | warm(2回目)のTTFT |
|---|---|---|
| Ollama | 82.5% | 308ms |
| vLLM (prefix cache) | 88.5% | 59ms |
| SGLang (RadixAttention) | 80.2% | 170ms |
削減率で見ると、Ollamaも82.5%とちゃんと効いています。1人で同じ資料に連投するだけなら、Ollamaでも体感は改善します。
差が出るのは**warmの絶対値(下限の速さ)**です。2回目のTTFTがvLLMは59ms、SGLangは170msなのに対し、Ollamaは308ms——サーバ勢が5〜10倍速い。連投の頻度が高い・レイテンシにこだわるなら、ここが効いてきます。
おまけに、prefixの長さによってエンジンの得意が分かれます:
- vLLM:短いsystem prompt(〜512token)の再利用でも55%効く
- SGLang:短いと効かない(1%)が、2k〜4kの文書で一気に効く(85〜91%)
- Ollama:その中間
→ 同じ資料に高頻度で質問する/warmの下限速度を詰めたいなら、vLLM・SGLangのprefix再利用を見る価値あり。
短いプロンプト再利用ならvLLM、中規模RAG文書の再利用ならSGLang、が今回の傾向。
壁③:長文contextの壁(実は「入らない」のではない)
症状:「長い資料を貼ったのに、途中の内容を忘れている/切れている気がする」
これは誤解されがちですが、Ollamaが長文を扱えないわけではありません。犯人は 既定の num_ctx が 2048 であること。これを超えた分は黙って切り捨てられます。気づかないうちに資料の後半が無視されている、というやつです。
Ollamaの既定 num_ctx は 2048 token。長いPDFや資料を貼っても、ここを超えた部分はエラーも出さずに切り捨てられます。「途中を忘れている」の正体はたいていこれです。
num_ctxを上げればちゃんと載ります。RTX 4070 (12GB) で Ollama 4B(Q4) を測ると:
| num_ctx | ピークVRAM (既定KV f16) | KV量子化時 (q8_0) |
|---|---|---|
| 2,048(既定) | 5.9GB | 5.9GB |
| 32,768 | 7.1GB | 6.7GB |
| 98,304 | 9.9GB | 8.8GB |
96k tokenのcontextでも12GBに載ります。さらに OLLAMA_KV_CACHE_TYPE=q8_0 でKVキャッシュを量子化すると、大きなcontextで約1GB節約できます(vLLMの「FP8 KVキャッシュ」と同じ発想で、Ollamaにもこのノブはあります。ただ隠れているだけ)。
Ollamaで num_ctx を上げる / KVキャッシュを量子化する
# サーバ側: flash attention on + KVキャッシュ q8_0(大きい context で VRAM 節約)
OLLAMA_FLASH_ATTENTION=1 OLLAMA_KV_CACHE_TYPE=q8_0 ollama serve
# リクエスト側: num_ctx を都度指定(native /api/chat の options)
curl http://localhost:11434/api/chat -d '{
"model": "qwen3.5:4b",
"messages": [{"role": "user", "content": "<長い資料> ... 要約して"}],
"stream": true,
"think": false,
"options": { "temperature": 0, "num_ctx": 32768 }
}'
num_ctx は Modelfile の PARAMETER num_ctx 32768 で既定化することもできます。
つまりこの壁は「Ollamaでは無理」ではなく「既定値の罠と、隠れたノブを知る必要がある」話です。
→ 長文contextは
num_ctxを上げればOllamaでも扱えます。
ただし「設定を露出して詰める/OOMを管理する」のは、vLLM・SGLangのほうが素直。
本格的な長文運用に踏み込むなら、サーバ側のmax_model_lenや FP8 KV の設計が効いてきます(次回詳しく)。
vLLM / SGLang では何が"見える"のか
ここまでの「症状」を数値で見られるのが、vLLM・SGLangの価値です。単なるtok/sではなく:
- TTFT(最初の1tokenまで):チャット・RAGの体感に直結
- ITL / TPOT(token間の間隔):生成中のなめらかさ
- prefill / decode:入力処理と出力生成のどちらが重いか
- p95 / p99 latency:平均ではなく「遅いリクエストがどれだけ待つか」(複数人運用で重要)
- aggregate throughput:サーバ全体の処理量(並列で重要)
そして、これらを制御するノブ(max_model_len、KVキャッシュ、prefix cache、バッチング、並列数)が表に出ています。
- vLLM:OpenAI互換APIで導入が素直。PagedAttention / prefix caching / FP8 KV。
- SGLang:RadixAttentionによるprefix再利用、そして並列時のTTFT/p95が今回際立って強い。
どちらが常に速い、という話ではなく、当たった壁によって見るエンジンが変わる、が正確なところです(今回の並列に関しては、データ上SGLangが明確に有利でした)。
移行コストの正直な話
vLLM/SGLangを万能扱いはしません。Ollamaに比べて面倒は増えます。
- Docker起動、
gpu-memory-utilizationやmax-model-lenの調整、OOMとの戦い - モデル形式が変わる(OllamaのGGUF → HFのFP8 / AWQ など)
ただし救いもあります。どちらもOpenAI互換APIなので、アプリ側のコードはほぼそのまま流用できます。 「サーバを差し替える」感覚で試せます。
Ollamaも含め、いずれも /v1/chat/completions(OpenAI互換)を喋れます。クライアント側は接続先URLとモデル名を変えるだけ。「まず試す」ハードルは見た目ほど高くありません。
起動はだいたい docker run 一発です(今回の計測で使った構成の例)。
vLLM の起動例(RTX 4070, Qwen3.5-4B FP8)
docker run -d --name vllm --gpus all -p 8000:8000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--shm-size 1g \
vllm/vllm-openai:nightly \
vllm serve RedHatAI/Qwen3.5-4B-FP8-dynamic \
--attention-backend flashinfer \
--enable-prefix-caching \
--max-model-len 16384 \
--max-num-batched-tokens 4096 \
--gpu-memory-utilization 0.90 \
--enforce-eager
# -> http://localhost:8000/v1/chat/completions
⚠️ RTX 4070 (12GB) で 4B を載せるなら、CUDA graph が OOM しやすいので
--enforce-eager、--gpu-memory-utilizationは 0.90 前後が安全。詳細は次回の設定チートシートで。
SGLang の起動例(同上)
docker run -d --name sglang --gpus all --shm-size 32g --ipc=host -p 30000:30000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
lmsysorg/sglang:latest \
python3 -m sglang.launch_server \
--model-path RedHatAI/Qwen3.5-4B-FP8-dynamic \
--host 0.0.0.0 --port 30000 \
--context-length 16384 \
--mem-fraction-static 0.85 \
--max-running-requests 16 \
--attention-backend flashinfer
# RadixAttention は既定で有効。-> http://localhost:30000/v1/chat/completions
なので判断は、「今の痛み(壁)が、このセットアップコストを上回るか」で決めるのが現実的です。
まとめ:どの壁ならOllama、どの壁ならサーバ
| あなたの使い方 | 推奨 | 理由(今回の実測) |
|---|---|---|
| 1人・短文チャット / モデル試用 | Ollama | 1リクエストで最速・最軽量(91 tok/s, 6GB) |
| 1人・同じ資料に連投(軽いRAG) | Ollamaで可(詰めるならサーバ) | prefix再利用は効く(-82%)が、warm下限はサーバが5-10x速い |
| 複数人・並列リクエスト | SGLang(次点 vLLM) | Ollamaは直列化。p95 TTFTで約100倍、スループット7倍の差 |
| 長文を入れたい | Ollama(num_ctx↑) or サーバ |
96kも載る。本格運用は設定を露出できるサーバが楽 |
| 最初の返事の速さを詰めたい | SGLang | 並列8でもp95 TTFT 0.28秒 |
最後に大事な補足を3つ:
- 並列の壁は「Ollamaがダメ」ではなく「Ollama v0.24.0 が Qwen3.5 系を
num_parallel>1非対応扱いにして直列化する」という具体的(かつバージョン依存)の話です。 - 長文の壁は「入らない」ではなく「既定
num_ctx2048 の罠」です。 - 品質面では、今回4Bで測った全エンジン(Ollama / vLLM / SGLang、約1700応答)で反復ループなどの劣化は出ていません。速度の差は品質を犠牲にした差ではありません。
Ollamaはローカルの入口として本当に優秀です。 そのうえで、RAG・複数人・低レイテンシを気にし始めたら、「どのモデルが速いか」ではなく「prefill・decode・TTFT・p95・KVのどこが詰まっているか」を見る段階。そこでvLLM / SGLangが次の選択肢になります。
計測条件(再現用)
- GPU: RTX 4070 (12GB)、モデル: Qwen3.5-4B
- Ollama:
qwen3.5:4b(Q4_K_M, 既定) /qwen3.5:4b-q8_0(Q8_0, 精度をFP8に合わせた公平比較用) - vLLM / SGLang:
RedHatAI/Qwen3.5-4B-FP8-dynamic(FP8)
- Ollama:
- Ollamaはフェアに調整:flash attention on、
think=false、num_ctxをケース毎に確保、OLLAMA_NUM_PARALLELを並列数に設定 -
temperature 0、warmup 1 + 計測5回、median / p95 集計 - 並列・prefixの条件は vLLM/SGLang 側の既存ベンチと同一プロンプト・同一トークン長で統一