Repository navigation
Conversation
…red) Two upstream PRs taken into the daily branch, both clean merges: FlashML-org#278 bernimccoy fix(moe): match the benchbw verdict to the served model's expert size FlashML-org#341 tuxevil feat(bench): add reproducible serving performance harness Chosen on one criterion: does it still hold when the hardware changes. FlashML-org#278 is the one that bites. `load_backend_recommendation` keyed the `--moe-backend auto` verdict on the expert FORMAT alone, and `ft bench bw --dtype` measures one geometry per format (nvfp4: 8.0 MB experts). The CPU MoE kernel has a per-expert fixed cost, so that verdict overstates the CPU side for a small-expert model -- 90 GB/s at the dtype geometry vs 32 GB/s at Qwen3.6-35B-A3B's 1.7 MB experts on the same box, and auto then picks hybrid where offload decodes 3x faster (27 vs 84 tok/s on a 4070 SUPER). The PR adds `_comparable()` so a dtype verdict is only reused when the benched expert size is within a factor of the served model's. Ornith-1.5-35B-A3B is on the small-expert side, so this box is in the affected class; the practical effect here is not yet measured. FlashML-org#341 is a pure addition (+958/-0): benchmarks/bench_serving.py measures TTFT, decode, effective prefill and total latency against a running server, and records model, server, hardware, KV/MoE/Mamba geometry, MoE residency and the cache budget with the numbers. It is the instrument for comparing a box before and after a change, which is why it is here rather than in the "nice to have" pile. Full suite, one process: 12 failed, 1812 passed, 57 skipped. try/all at c9242db fails the same 12 by name (diff of the sorted FAILED lists is empty) with 1798 passed, so this adds 14 passing tests and breaks none. Assisted-by: Claude Opus 5
|
We exercised this benchmark shape on In our RTX 5090 run, c8 delivered 115.249 aggregate output tok/s and 32.428 median steady decode tok/s/request. A separate c16 run with both Would you be open to an extension that adds configurable concurrency levels and records, per level:
We can prepare the patch against this branch if that scope fits your intent for #341. Published evidence and the exact c1/c2/c4/c8/c16 records are here: The c16 prefill figure in that artifact is explicitly client-wall/effective rather than engine-internal telemetry, consistent with this PR's measurement boundary. |
|
Thanks — this is exactly the kind of follow-up the schema was intended to support. The c8/c16 results make the case clearly: aggregate throughput alone hides the per-session latency collapse, and without recording the active-request and CUDA graph limits it is easy to mislabel an offered concurrency level as an admitted one. I would welcome a focused concurrency extension built on top of this harness. The fields you listed fit well with the current measurement boundary, especially:
My preference would be to keep #341 focused on the single-stream serving protocol and schema foundation, then implement the concurrency sweep as a follow-up PR based on this branch. That should let us settle the admission/queue semantics explicitly instead of making them implicit in the first version. The RTX 5090 data is a very useful reference case. In particular, the contrast between c8 and the fully admitted c16 run is exactly the kind of result this harness should make easy to reproduce and compare. |
…ing performance harness Assisted-by: Claude Opus 5.5
… bearer header next serves an --api-key (FlashML-org#305) that answers 401 to unauthenticated requests, so the harness could not measure such a server. Take --api-key (default $FREETOKEN_API_KEY, like ft serve) and send it on every request. Assisted-by: Claude Opus 5.5
…report as a miss when reporting is on The server omits prompt_tokens_details for a zero prefix-cache hit (sglang convention), so every fresh prompt looked like a server without --enable-cache-report and the fresh-prefill rate, the harness's main number, was always n/a. Probe once whether a repeated prompt reports a hit; when it does, an absent object is a miss. Without reporting the hit columns stay n/a. Assisted-by: Claude Opus 5.5
Summary
benchmarks/bench_serving.py, a reproducible client-wall performance harness for an already-running FreeToken server./v1/messages/count_tokens./v1/chat/completionsTTFT, decode throughput, effective prefill throughput, and total latency.Motivation
FreeToken currently has serving benchmarks, but no small, generic harness that clearly separates client-observed performance from engine-internal telemetry.
This harness records measurements at the HTTP client boundary. Fields prefixed with
client_wall_include queueing, prefill, scheduling, detokenization, and SSE delivery.Design
The harness:
/health,/v1/models,/v1/cache/status, and/v1/stats.fresh→identical→small_suffixsequence.The benchmark uses explicit greedy sampling with thinking disabled so runtime measurements are not affected by reasoning nondeterminism.
Decode throughput follows the existing serving benchmark convention:
Scope
This first version intentionally:
These capabilities can build on the schema in later PRs.
Validation
PYTHONPATH=python:. uv run --no-project --with pytest pytest -q tests/benchmarks/test_bench_serving.pyuv run --no-project --with ruff ruff check benchmarks/bench_serving.py tests/benchmarks/test_bench_serving.pyuv run --no-project --with ruff ruff format --check benchmarks/bench_serving.py tests/benchmarks/test_bench_serving.pypython -m py_compile benchmarks/bench_serving.pygit diff --checkThe full repository test suite was not collected in this environment because FreeToken's runtime dependencies, including PyTorch, are not installed.