bench_ollama: add --think on|off|auto for cross-family comparison
The curator scenario hardcoded think=true, which is qwen3-family-specific. Non-qwen3 models silently ignore the field, so cross-family curator comparisons were apples-to-oranges (qwen thinks, others don't). New --think flag: - auto (default): scenario-driven — chat=off, curator=on. Matches the prior behaviour and the most common case. - off: force disabled across all runs. Use for fair cross-family comparison; aligns behaviour explicitly even though non-qwen models would ignore think anyway. - on: force enabled across all runs. Use to measure what think contributes on the same model (paired runs: --think off then on). Output markdown table now records the think mode used, so saved results are self-documenting when you diff cross-server or cross-config. Docstring + usage examples updated to reflect the qwen3 candidate set the bench was originally tuned for. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+74
-15
@@ -13,18 +13,26 @@ Scenarios:
|
|||||||
chat — small input, small output, no thinking. Mirrors the chat-only
|
chat — small input, small output, no thinking. Mirrors the chat-only
|
||||||
journal companion's expected load (short user message →
|
journal companion's expected load (short user message →
|
||||||
curious follow-up).
|
curious follow-up).
|
||||||
curator — longer transcript input, structured-output extraction, with
|
curator — longer transcript input, structured-output extraction.
|
||||||
thinking enabled. Mirrors the curator's expected load
|
Thinking defaults ON (qwen3 family) but is overridable via
|
||||||
(read recent conversation → emit captures).
|
--think for cross-family comparisons.
|
||||||
|
|
||||||
|
Think modes:
|
||||||
|
auto — chat=off, curator=on (default; matches scenario intent).
|
||||||
|
off — force think disabled for all runs (fair cross-family
|
||||||
|
comparison; non-qwen3 models silently ignore the field
|
||||||
|
anyway, so this aligns behaviour explicitly).
|
||||||
|
on — force think enabled for all runs (measures what think
|
||||||
|
contributes vs `off` on the same model).
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
- Ollama running and reachable at --server (default http://localhost:11434).
|
- Ollama running and reachable at --server (default http://localhost:11434).
|
||||||
- The models named in --models must already be pulled
|
- The models named in --models must already be pulled
|
||||||
(`ollama pull qwen2.5:32b` etc).
|
(`ollama pull qwen3:30b-a3b` etc).
|
||||||
|
|
||||||
Usage examples:
|
Usage examples:
|
||||||
# Curator candidate on CPU, 3 runs each (default), one model:
|
# Curator candidate on CPU, 3 runs each (default), one model:
|
||||||
python scripts/bench_ollama.py --models qwen2.5:32b --scenario curator
|
python scripts/bench_ollama.py --models qwen3:30b-a3b --scenario curator
|
||||||
|
|
||||||
# Chat candidate on GPU, against a remote server:
|
# Chat candidate on GPU, against a remote server:
|
||||||
python scripts/bench_ollama.py \\
|
python scripts/bench_ollama.py \\
|
||||||
@@ -32,10 +40,16 @@ Usage examples:
|
|||||||
--models llama3.2:3b \\
|
--models llama3.2:3b \\
|
||||||
--scenario chat --num-gpu 99
|
--scenario chat --num-gpu 99
|
||||||
|
|
||||||
# Compare three curator candidates on CPU, 5 runs each, write markdown:
|
# Compare two qwen curator candidates on CPU, 5 runs each:
|
||||||
python scripts/bench_ollama.py \\
|
python scripts/bench_ollama.py \\
|
||||||
--models qwen2.5:32b,qwen3:14b,gemma2:27b \\
|
--models qwen3:30b-a3b,qwen3:32b \\
|
||||||
--scenario curator --runs 5 --out bench-cpu.md
|
--scenario curator --runs 5 --out bench-qwen-curator.md
|
||||||
|
|
||||||
|
# Cross-family curator comparison with think forced OFF (apples-to-apples):
|
||||||
|
python scripts/bench_ollama.py \\
|
||||||
|
--models qwen3:30b-a3b,gemma2:27b,mistral-small:22b \\
|
||||||
|
--scenario curator --think off --runs 3 \\
|
||||||
|
--out bench-curator-no-think.md
|
||||||
|
|
||||||
The first request to a (model, num_gpu) pair triggers a model load and is
|
The first request to a (model, num_gpu) pair triggers a model load and is
|
||||||
excluded from the timing as a warm-up. Subsequent runs reflect warm-cache
|
excluded from the timing as a warm-up. Subsequent runs reflect warm-cache
|
||||||
@@ -145,23 +159,36 @@ class ScenarioResult:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_think(scenario: str, think_mode: str) -> bool:
|
||||||
|
"""Map (scenario, --think mode) → boolean think flag."""
|
||||||
|
if think_mode == "on":
|
||||||
|
return True
|
||||||
|
if think_mode == "off":
|
||||||
|
return False
|
||||||
|
# auto: scenario-driven
|
||||||
|
return scenario == "curator"
|
||||||
|
|
||||||
|
|
||||||
def build_request(
|
def build_request(
|
||||||
scenario: str, model: str, num_gpu: int, keep_alive: str
|
scenario: str,
|
||||||
|
model: str,
|
||||||
|
num_gpu: int,
|
||||||
|
keep_alive: str,
|
||||||
|
think_mode: str = "auto",
|
||||||
) -> dict:
|
) -> dict:
|
||||||
if scenario == "chat":
|
if scenario == "chat":
|
||||||
messages = [
|
messages = [
|
||||||
{"role": "system", "content": SYSTEM_PROMPT_CHAT},
|
{"role": "system", "content": SYSTEM_PROMPT_CHAT},
|
||||||
{"role": "user", "content": USER_MESSAGE_CHAT},
|
{"role": "user", "content": USER_MESSAGE_CHAT},
|
||||||
]
|
]
|
||||||
think = False
|
|
||||||
elif scenario == "curator":
|
elif scenario == "curator":
|
||||||
messages = [
|
messages = [
|
||||||
{"role": "system", "content": SYSTEM_PROMPT_CURATOR},
|
{"role": "system", "content": SYSTEM_PROMPT_CURATOR},
|
||||||
{"role": "user", "content": USER_TRANSCRIPT_CURATOR},
|
{"role": "user", "content": USER_TRANSCRIPT_CURATOR},
|
||||||
]
|
]
|
||||||
think = True
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"unknown scenario: {scenario}")
|
raise ValueError(f"unknown scenario: {scenario}")
|
||||||
|
think = _resolve_think(scenario, think_mode)
|
||||||
return {
|
return {
|
||||||
"model": model,
|
"model": model,
|
||||||
"messages": messages,
|
"messages": messages,
|
||||||
@@ -226,12 +253,15 @@ def benchmark(
|
|||||||
runs: int,
|
runs: int,
|
||||||
num_gpu: int,
|
num_gpu: int,
|
||||||
keep_alive: str,
|
keep_alive: str,
|
||||||
|
think_mode: str,
|
||||||
) -> list[ScenarioResult]:
|
) -> list[ScenarioResult]:
|
||||||
results: list[ScenarioResult] = []
|
results: list[ScenarioResult] = []
|
||||||
for model in models:
|
for model in models:
|
||||||
for scenario in scenarios:
|
for scenario in scenarios:
|
||||||
sr = ScenarioResult(model=model, scenario=scenario)
|
sr = ScenarioResult(model=model, scenario=scenario)
|
||||||
payload = build_request(scenario, model, num_gpu, keep_alive)
|
payload = build_request(
|
||||||
|
scenario, model, num_gpu, keep_alive, think_mode
|
||||||
|
)
|
||||||
# Warm-up run loads the model into RAM/VRAM with the requested
|
# Warm-up run loads the model into RAM/VRAM with the requested
|
||||||
# num_gpu setting. Excluded from the measured runs because it
|
# num_gpu setting. Excluded from the measured runs because it
|
||||||
# otherwise dominates TTFT with model-load time.
|
# otherwise dominates TTFT with model-load time.
|
||||||
@@ -268,15 +298,27 @@ def benchmark(
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def format_markdown(results: list[ScenarioResult], *, server: str, num_gpu: int) -> str:
|
def format_markdown(
|
||||||
|
results: list[ScenarioResult],
|
||||||
|
*,
|
||||||
|
server: str,
|
||||||
|
num_gpu: int,
|
||||||
|
think_mode: str,
|
||||||
|
) -> str:
|
||||||
mode = "CPU only" if num_gpu == 0 else (
|
mode = "CPU only" if num_gpu == 0 else (
|
||||||
f"GPU offload ({num_gpu} layers)" if num_gpu > 0 else "Ollama default"
|
f"GPU offload ({num_gpu} layers)" if num_gpu > 0 else "Ollama default"
|
||||||
)
|
)
|
||||||
|
think_label = {
|
||||||
|
"auto": "auto (chat=off, curator=on)",
|
||||||
|
"on": "forced ON",
|
||||||
|
"off": "forced OFF",
|
||||||
|
}.get(think_mode, think_mode)
|
||||||
lines = [
|
lines = [
|
||||||
"# Ollama benchmark",
|
"# Ollama benchmark",
|
||||||
"",
|
"",
|
||||||
f"- Server: `{server}`",
|
f"- Server: `{server}`",
|
||||||
f"- Mode: {mode} (`num_gpu={num_gpu}`)",
|
f"- Hardware mode: {mode} (`num_gpu={num_gpu}`)",
|
||||||
|
f"- Think: {think_label}",
|
||||||
"",
|
"",
|
||||||
"| Model | Scenario | Runs | Prompt tok | TTFT p50 (ms) "
|
"| Model | Scenario | Runs | Prompt tok | TTFT p50 (ms) "
|
||||||
"| Total p50 (ms) | tok/s p50 | Output tok (mean) |",
|
"| Total p50 (ms) | tok/s p50 | Output tok (mean) |",
|
||||||
@@ -337,6 +379,17 @@ def main():
|
|||||||
default="10m",
|
default="10m",
|
||||||
help="Ollama keep_alive (default %(default)s)",
|
help="Ollama keep_alive (default %(default)s)",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--think",
|
||||||
|
choices=["auto", "on", "off"],
|
||||||
|
default="auto",
|
||||||
|
help=(
|
||||||
|
"Think-mode control. auto = chat off / curator on (default). "
|
||||||
|
"off = force disabled for fair cross-family comparison "
|
||||||
|
"(non-qwen3 models silently ignore think anyway). "
|
||||||
|
"on = force enabled to measure what think contributes."
|
||||||
|
),
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--out",
|
"--out",
|
||||||
help="Write markdown table to this file (also prints to stdout)",
|
help="Write markdown table to this file (also prints to stdout)",
|
||||||
@@ -355,9 +408,15 @@ def main():
|
|||||||
runs=args.runs,
|
runs=args.runs,
|
||||||
num_gpu=args.num_gpu,
|
num_gpu=args.num_gpu,
|
||||||
keep_alive=args.keep_alive,
|
keep_alive=args.keep_alive,
|
||||||
|
think_mode=args.think,
|
||||||
)
|
)
|
||||||
|
|
||||||
md = format_markdown(results, server=args.server, num_gpu=args.num_gpu)
|
md = format_markdown(
|
||||||
|
results,
|
||||||
|
server=args.server,
|
||||||
|
num_gpu=args.num_gpu,
|
||||||
|
think_mode=args.think,
|
||||||
|
)
|
||||||
print("\n" + md)
|
print("\n" + md)
|
||||||
if args.out:
|
if args.out:
|
||||||
with open(args.out, "w") as f:
|
with open(args.out, "w") as f:
|
||||||
|
|||||||
Reference in New Issue
Block a user