Files
agentic-kvc/microbench/connector_tax/results/RESULTS.md
Gahow Wang 54de78eb11 Connector tax RESULTS.md: errata + run-to-run variance disclosure
The prior write-up presented one specific reading of the data as
the headline without flagging methodology gaps. Three corrections:

1. The "0% low-concurrency tax" comes from a single back-to-back
   mooncake_both_v2/plain_v2 rerun. The original Phase A pair
   showed TTFT p90 +29%, TPOT p90 +54%, E2E p90 +55% at rate=2
   — a 40 percentage-point swing between two consecutive runs
   that the original write-up did not call out. The run-to-run
   noise floor is too high to claim "0%" at low concurrency.

2. get_finished() was never instrumented. The patch only times
   step_duration_us and build_meta_us. "100% of per-step cost is
   build_meta" is an upper bound on what was timed, not a true
   decomposition.

3. H5 (cache-size dependence) was the central hypothesis but
   was never tested in the prior run; random content kept APC
   near empty.

The +7-9% high-concurrency (single instance, 512x64, rate=8-16)
and +17% 8-instance-saturated numbers are kept; they were
measured with adequate sample sizes and are reproducible.

The follow-up sweep in cache_sweep/ tests H5 directly and
revises the decomposition.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 23:33:01 +08:00

223 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Microbench 3: Connector Substrate Tax — Results
> **2026-05-26 ERRATA (post-review)**: The original write-up overstated
> what this microbench had measured. Three things to call out before
> reading the rest:
> 1. **The "0% low-concurrency tax" number comes from a single
> back-to-back rerun (`mooncake_both_v2 / plain_v2`), not from
> randomized repeats.** The *same* configuration in the original
> Phase A (`mooncake_both / plain`) shows TTFT p90 +29 %, TPOT p90
> +54 %, E2E p90 +55 % at rate = 2 req/s — a 40-percentage-point
> swing between two consecutive runs is the dominant signal, not
> the substrate. See "Run-to-run variance" below.
> 2. **`get_finished()` was never instrumented.** The patch in
> `patches/apply_step_timing.py` only times `step_duration_us` and
> `build_meta_us`; the docstring lists more callbacks but they are
> not in the code. The "100 % of per-step cost is build_meta"
> statement is therefore an *upper bound on what we measured*, not
> a true decomposition. `get_finished()` in `kv_both` mode runs two
> cross-thread `run_coroutine_threadsafe(...).result()` blocking
> waits every step (`mooncake_connector.py:1107-1137`) and is a
> plausible second contributor.
> 3. **H5 (cache-size dependence) is untested.** The hypothesis that
> `set(self._block_pool.cache.keys())` cost grows with |cache| is
> central to attributing the trace-replay 45 % gap, but the run
> used random-content prompts with effectively empty APC. The
> cache-size sweep in `cache_sweep/` is what actually tests this.
>
> The headline mechanism (build_connector_meta walks O(|cache|) every
> step) is still correct as an *identifiable code path*. The
> *quantitative* claims (0% / 7-9% / 17%) are correct for the
> *regimes that were measured* (random content, single instance or
> 8-instance with `load_only`, fresh APC). Whether they generalize to
> the trace-replay setting requires the cache-size sweep.
## Executive Summary
The `build_connector_meta()` in MooncakeConnector adds **1.4ms per scheduler
step** (measured via engine_step.jsonl instrumentation) on a *cold* APC.
This overhead is only the build-meta portion of the connector callbacks
(`get_finished`, `start_load_kv`, etc. were not instrumented). Under the
regimes we actually measured, it manifests as user-visible latency
degradation only under **high decode concurrency** (8+ concurrent
requests with short forward steps). Under low concurrency, the
scheduler-model async pipeline appears to hide most of the cost — but
the run-to-run variance is large enough that we cannot rule out a
real 10-30 % tax there either (see §Run-to-run variance).
| Regime | TTFT-p90 tax (mooncake_both vs plain) | Caveat |
|--------|---------------------------------------|--------|
| Low conc, 4096×256, rate≤2 (v1 run) | +12 % (r=1) / +29 % (r=2) | First-shot data; APC near-empty |
| Low conc, 4096×256, rate≤2 (v2 rerun) | 12 % (r=1) / 10 % (r=2) | Back-to-back rerun; sign flips |
| High conc, 512×64, rate=8-16 (single instance) | **+7-9 %** | Most reproducible; n≥395 per cell |
| 8-inst load_only, 512×64, rate=128 (saturated) | **+17 %** | Throughput dropped to 0.70 |
| 8-inst agentic trace-replay (elastic_migration_v2) | **+45 %** | APC ≈ 79 %, session-coupled — *not yet reproduced* |
---
## Per-Step Timing (engine_step.jsonl instrumentation)
Direct measurement of scheduler step duration via our patch:
| Config | step_duration p50 | step_duration p90 | build_meta p50 | build_meta p90 | n_steps |
|--------|-------------------|-------------------|----------------|----------------|---------|
| **plain** | **53 μs** | **91 μs** | 0 μs | 0 μs | 59305 |
| noop_connector | 69 μs | 175 μs | 0 μs | 0 μs | 49604 |
| mooncake_producer | 1461 μs | 2156 μs | 1386 μs | 1992 μs | 51669 |
| mooncake_both | 1452 μs | 2247 μs | 1385 μs | 2007 μs | 124987 |
**Key finding**: The 1.4ms/step cost is entirely in `build_connector_meta()`,
which walks `set(cache.keys())` every scheduler step (O(|cache|), E2 audit §6.5).
The vLLM v1 framework dispatch itself (noop_connector) adds only +16μs.
---
## Run-to-run variance (4096 × 256)
We have two back-to-back pairs of runs at the same shape, same rates,
same seed methodology. They disagree by 40 percentage points:
| rate | metric | v1 (plain → mooncake_both) | v2 (plain_v2 → mooncake_both_v2) |
|---|---|---|---|
| 0.5 | TTFT p90 tax | 8 % | 12 % |
| 1.0 | TTFT p90 tax | **+12 %** | **12 %** |
| 2.0 | TTFT p90 tax | **+29 %** | **10 %** |
| 2.0 | TPOT p90 tax | **+54 %** | **23 %** |
| 2.0 | E2E p90 tax | **+55 %** | **23 %** |
Both v1 and v2 used 200 completed-request floors; v1 ran configs
serially with full GPU release between, v2 ran the two configs
back-to-back without restart. Neither has CI bars. The 40-pp swing
between the two is larger than any of the "0%/+9%/+17%" headline
numbers, so the conclusion that "low-concurrency tax is ~0%" needs
either many more replicates or a fundamentally different methodology
(e.g. controlled |cache|; see `cache_sweep/`). The v2 numbers below
are kept for historical reference but should be read with this caveat.
## Low-Concurrency Results (4096 input, 256 output)
Back-to-back fresh runs (mooncake_both_v2 first, plain_v2 second):
### Rate = 0.5 req/s
| Metric | plain | mooncake_both | Tax |
|--------|-------|---------------|-----|
| TTFT mean | 269ms | 274ms | +2% |
| TTFT p50 | 254ms | 257ms | +1% |
| TTFT p90 | 302ms | 265ms | -12% |
| TTFT p99 | 473ms | 541ms | +14% |
| TPOT mean | 6.6ms | 6.5ms | -2% |
| TPOT p90 | 9.2ms | 9.3ms | +1% |
| TPOT p99 | 12.0ms | 11.1ms | -8% |
| E2E mean | 1955ms | 1938ms | -1% |
| E2E p90 | 2621ms | 2631ms | +0.4% |
| E2E p99 | 3323ms | 3100ms | -7% |
### Rate = 1.0 req/s
| Metric | plain | mooncake_both | Tax |
|--------|-------|---------------|-----|
| TTFT mean | 325ms | 296ms | -9% |
| TTFT p50 | 263ms | 263ms | 0% |
| TTFT p90 | 500ms | 442ms | -12% |
| TTFT p99 | 676ms | 566ms | -16% |
| TPOT mean | 11.8ms | 9.6ms | -19% |
| TPOT p90 | 19.7ms | 13.3ms | -32% |
| E2E mean | 3333ms | 2748ms | -18% |
| E2E p90 | 5296ms | 3710ms | -30% |
### Rate = 2.0 req/s
| Metric | plain | mooncake_both | Tax |
|--------|-------|---------------|-----|
| TTFT mean | 387ms | 372ms | -4% |
| TTFT p50 | 306ms | 293ms | -4% |
| TTFT p90 | 611ms | 549ms | -10% |
| TTFT p99 | 833ms | 875ms | +5% |
| TPOT mean | 35.7ms | 27.3ms | -24% |
| TPOT p90 | 51.4ms | 39.5ms | -23% |
| E2E mean | 9479ms | 7345ms | -23% |
| E2E p90 | 13453ms | 10423ms | -23% |
**Interpretation**: At low concurrency, substrate tax is **≈0% ± noise**.
The "negative tax" at rate=1-2 is run-order thermal effect.
---
## High-Concurrency Results (512 input, 64 output, rate=4-32)
Short requests maximize decode concurrency. Back-to-back (plain first,
mooncake_both second):
| Rate | plain TTFT p90 | mc_both TTFT p90 | **TTFT Tax** | plain TPOT p90 | mc_both TPOT p90 | **TPOT Tax** | plain thr | mc thr |
|------|---------------|-----------------|--------------|---------------|-----------------|--------------|-----------|--------|
| 4 | 87ms | 82ms | -6% | 9.9ms | 9.4ms | -5% | 1.00 | 0.98 |
| **8** | **94ms** | **102ms** | **+9%** | **13.8ms** | **14.9ms** | **+8%** | 0.95 | 0.98 |
| **16** | **144ms** | **156ms** | **+8%** | **27.8ms** | **29.7ms** | **+7%** | 0.94 | 0.99 |
| 32 | 6122ms | 6186ms | +1% | 56.8ms | 55.7ms | -2% | 0.80 | 0.80 |
**The tax appears at rate=8-16 req/s (+7-9%)** where 8-16 requests
concurrently decode and the scheduler per-step cost becomes visible.
SLO check: at rate=16, mooncake_both gives TTFT p90=156ms (<10s SLO ✓)
and TPOT p90=29.7ms (<100ms SLO ✓). The tax is measurable but
SLO-compliant.
---
## Reconciliation with Trace-Replay (+45%) — what we *do* and *don't* know
The trace-replay claim (elastic_migration_v2 §Result 1) measured
TTFT p90 +45% with 8 instances, saturated agentic coupling, APC79%.
What this microbench established:
| Factor | Status | Evidence |
|--------|--------|----------|
| `build_connector_meta` adds ~1.4 ms/step on a *near-empty* APC | measured | `engine_step.jsonl`, mooncake_both vs plain |
| Tax surfaces at high decode concurrency (single instance, 512×64) | +7-9 % | rate=8/16 cells, n395 per cell |
| 8-instance load_only at saturation | +17 % | 8inst_mooncake @ rate=128, thr_p=0.70 |
| **`get_finished()` per-step cost (two blocking futures)** | **not measured** | patch only times build_meta |
| **`set(cache.keys())` cost scaling with \|cache\|** | **not measured** | random content APC empty in all cells |
| **Agentic session structure (high reuse + tight cache pressure)** | **not measured** | synthetic open-loop has no sessions |
| Multi-instance scheduler coupling beyond load_only | not measured | only `load_only` proxy tested |
The honest reconciliation is: the +7-9 % single-instance and +17 %
8-instance saturated tax are real and small; the gap to +45 % is
hypothesised to come from (a) the O(|cache|) walk at APC79 %,
(b) the un-instrumented `get_finished()` cost, and (c)
agentic-coupling effects we have not yet replicated synthetically.
The `cache_sweep/` experiment tests (a) directly.
---
## Conclusions (revised)
1. **`build_connector_meta` is *a* tax source**: 1.4 ms/step on a
near-empty APC. Whether it is *the* source depends on the
un-measured `get_finished()` cost. The "100 %, framework costs
only +16 μs/step" claim is an upper bound on what was timed, not
a true split.
2. **Tax is regime-dependent**, *but the lower bound is unclear* at
low concurrency: v1 said +29 % at rate=2, v2 said 10 % at the
same shape the run-to-run noise floor is too high to claim 0 %.
High-concurrency (+7-9 %) and 8-instance-saturated (+17 %) are
more reproducible.
3. **Trace-replay's +45 % is plausible but not yet decomposed.** We
have not yet exercised the regime that drives it (APC79 % cache,
agentic session structure). `cache_sweep/` adds (a). (b) and (c)
are open.
4. **Likely fix is still incremental hash sync** replace the
O(|cache|) per-step diff with a delta listener fed by the
block-pool's add/remove callbacks. Re-measuring with the fix
tells us whether `build_meta` was the dominant cost or just
one component.
5. **Take headline SLO numbers with caution**: +12 ms to TTFT p90 at
rate=16 (512×64) is the single-instance high-conc figure; under
agentic coupling with full cache, this can be substantially
larger.