Files
agentic-kvc/analysis/research_findings.md
Gahow Wang 8e0c6e78b0 Add comprehensive research findings document
Synthesizes all experiments into a paper-ready analysis:
- Agentic workload characteristics vs chatbot/API
- Why PD-Sep, LMetric, elastic RDMA, chunk-size tuning don't work
- Why cache-aware session-sticky routing IS the key optimization
  (-60% TTFT, +24pp APC vs round-robin)
- System-level insights: prefill-decode interference threshold,
  Mooncake limitations, effective request weight after cache
- GPU balance → HEAVY TTFT -10.5% (demonstrated)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 07:16:31 +08:00

166 lines
8.4 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.

# Research Findings: KV Cache Optimization for Agentic LLM Workloads
**Date**: 2026-05-23
**Author**: Gahow Wang
---
## 1. Agentic Workload Characteristics (vs Chatbot/API)
| Property | Chatbot/API | **Agentic (this work)** |
|----------|-------------|----------------------|
| Input length | 1-5k tokens | **avg 33.6k, p90 88k** |
| Output length | 100-500 tokens | **avg 445** (similar) |
| I/O ratio | 1-10x | **75.6x** |
| Prefill token share | 50-70% | **98%** |
| KV reuse | Low (independent requests) | **71% theoretical, 91% intra-session** |
| Session structure | Mostly single-turn | **Multi-turn chains (parent_chat_id)** |
| Request weight distribution | Uniform | **Bimodal: 49% WARM/MEDIUM, 51% HEAVY** |
These characteristics fundamentally change what optimizations matter.
## 2. What Doesn't Work (and Why)
### 2.1 PD Disaggregation (DistServe/Splitwise approach)
**Setup**: 4P + 4D instances (Mooncake RDMA KV transfer)
**Result**: TTFT +72%, TPOT +1%, APC -5pp vs combined
**Root cause**: KV cache memory wall on decode instances. With avg 33.6k input and dedicated decode instances:
- Decode KV cache fills to 97.1%
- GPU idle (Running: 0), but new requests queue for KV cache memory
- 87.7% of TTFT is spent waiting for KV cache space
**Why it's different from chatbot**: Chatbot has short context (1-5k), so decode KV cache rarely fills. Agentic has 33k+ context, requiring 4-8GB KV per request → 2-3 concurrent requests saturate a single GPU's KV cache.
### 2.2 LMetric (OSDI'26, P_tokens × BS multiplication routing)
**Setup**: 8 instances, LMetric vs linear routing
**Result**: TTFT +2.2%, TPOT -4.4%, E2E +2.6% — all within noise (±7% run-to-run)
**Root cause**: Session affinity constrains routing freedom. LMetric's benefit (hyperparameter-free load balancing) is neutralized because turn 2+ requests MUST go to their session-sticky instance regardless of the scoring function. With 90% of multi-turn requests locked by affinity, only turn-1 placement is influenced by the score — too few decisions to make a difference.
### 2.3 Elastic P2P RDMA Offload (Heavy prefill on different instance)
**Setup**: 8 instances (kv_both), HEAVY requests prefilled on different instance, KV transferred via Mooncake RDMA
**Result**: E2E +37%, TPOT +11.6% — significantly worse
**Root causes**:
1. **Mooncake lacks layerwise KV transfer**: All blocks transferred after prefill completes (sequential, not pipelined). Transfer p50=1.1s for 40k tokens, highly variable (R²=0.095 vs input length).
2. **92% of HEAVY are turn-1 cold**: No cache to exploit on the P instance → full cold prefill is always slower than co-located cached prefill.
3. **kv_both has non-zero overhead at high concurrency**: Zero overhead at idle (Phase 0), but +16% TPOT at 16 sessions (background RDMA threads compete for resources).
### 2.4 Dedicated Prefill Service
**Setup**: 1 PS (no sessions) + 7 C (session-sticky)
**Result**: PS either gets 0 offloads (cost model correctly identifies it's more expensive) or gets too many (cascading timeouts)
**Root cause**: Without KV pull from C, PS does cold prefill (full input) which is always slower than C's cached prefill. With KV pull, double RDMA transfer overhead negates the benefit.
### 2.5 Chunk Size Tuning (max_num_batched_tokens)
**Setup**: 2048/4096/8192/16384 at 16 sessions
**Result**: Default 8192 is optimal; smaller chunks add scheduler overhead, larger chunks help HEAVY but hurt overall
### 2.6 OVERLOAD_FACTOR Tuning
**Setup**: 2.0/1.5/1.3/1.0 session affinity breaking threshold
**Result**: No effect — imbalance from workload skew, not routing
## 3. What DOES Work
### 3.1 Cache-Aware Session-Sticky Routing (the dominant optimization)
**Setup**: `score = ongoing_tokens - α × cache_hit_tokens`, session affinity for turn 2+
**Result vs round-robin**:
| Metric | Round-Robin | Cache-Aware | Delta |
|--------|------------|-------------|-------|
| TTFT p50 | 1.836s | **0.731s** | **-60%** |
| TPOT p90 | 0.086s | **0.073s** | **-15%** |
| APC | 20.8% | **44.7%** | **+24pp** |
**Why it works for agentic**: 91% of KV reuse is intra-session. Session-sticky routing ensures subsequent turns find their KV cache on the same instance. Cache-aware scoring steers turn-1 requests to instances with matching system prompt blocks (47% of blocks are shared across sessions).
### 3.2 GPU Balance → Latency Improvement (H4 evidence)
When GPU imbalance was reduced from 4.0x to 2.0x (via H4 cache-gate routing):
- HEAVY_COLO TTFT: 7.02s → **6.28s (-10.5%)**
- No TPOT regression
**Mechanism**: Hot GPU (63.3% util) causes queuing delays for co-located requests. Spreading load more evenly eliminates the queuing bottleneck.
**Limitation**: Only demonstrated for the 52/60 HEAVY requests that stayed co-located. The 8 offloaded requests had RDMA overhead. A routing-only approach to achieve balance (without RDMA) would be ideal.
## 4. System-Level Insights
### 4.1 Prefill-Decode Interference Threshold
| Concurrency | TPOT p90 | MEDIUM TPOT p90 | GPU Util |
|------------|----------|-----------------|----------|
| 8 sessions (1/GPU) | 0.073 | 0.075 | 30% |
| **16 sessions (2/GPU)** | **0.106 (+45%)** | **0.197 (+163%)** | 25% |
At >1 session per GPU, chunked prefill interference becomes significant. MEDIUM requests' TPOT degrades 2.5x because HEAVY prefill chunks block their decode steps.
### 4.2 Mooncake Transfer Engine Limitations
- **No layerwise transfer**: All KV blocks transferred after full prefill → pure sequential overhead
- **High variance**: R²=0.095 (transfer time uncorrelated with data size), bimodal distribution (0.6s or 18-30s)
- **Zero idle overhead**: kv_both mode has no cost when not transferring (Phase 0 validated)
- **Non-zero overhead at high concurrency**: +16% TPOT at 16 sessions (background threads)
### 4.3 KV Cache Reuse Structure
```
Total trace: 2.1M requests, 71% theoretical APC
Reuse breakdown:
91% intra-session (same session, subsequent turns)
4.8% cross-session (shared system prompts)
4.2% unique (no reuse)
Effective APC achieved:
Round-robin: 20.8% (destroys session locality)
Cache-aware: 44.7% (preserves session locality)
Theoretical max: 71% (infinite cache, single instance)
Gap: 26pp from eviction + routing imperfection
```
### 4.4 Request Weight After Cache
A critical insight: the "weight" of a request for scheduling should be `new_tokens = input - cached`, not `total_input`.
| Request | Total Input | After 70% Cache | Effective Weight |
|---------|------------|-----------------|-----------------|
| 80k HEAVY | 80k tokens | **24k tokens** | **MEDIUM** |
| 30k MEDIUM | 30k tokens | 9k tokens | WARM |
| 5k WARM | 5k tokens | 5k tokens | WARM |
This changes the scheduling picture: most "HEAVY" requests in agentic workloads are effectively MEDIUM after cache — PD separation's premise (heavy prefill needs dedicated resources) doesn't apply.
## 5. Paper-Ready Summary
### Agentic Workload Characteristics (vs prior LLM serving work):
1. **Extreme I/O ratio** (75x) → 98% of compute is prefill
2. **High intra-session KV reuse** (91%) → session affinity is critical
3. **Bimodal request weight** (51% HEAVY by input, but only ~15% HEAVY by new_tokens after cache)
4. **Multi-turn session structure** → routing decisions have long-term consequences (session migration destroys cache)
### Why existing approaches don't work:
1. **PD-Sep** assumes decode needs dedicated resources → agentic has memory wall on decode
2. **LMetric** assumes routing freedom → agentic has session affinity constraints
3. **Elastic RDMA** assumes KV transfer is cheap → Mooncake lacks layerwise pipelining
4. **Size-based classification** assumes HEAVY = needs special handling → after cache, most HEAVY is MEDIUM
### Our insights:
1. **Cache-aware session-sticky routing** is the dominant optimization: -60% TTFT, +24pp APC
2. **Routing quality > PD separation > eviction policy** (simulation verified: routing gives 24pp, eviction gives 1.8pp)
3. **Effective request weight** (new_tokens, not total_input) should drive scheduling
4. **Prefill-decode interference** only matters at >1 session/GPU (rarely reached in production clusters)
5. **GPU balance improvement** directly improves HEAVY TTFT (-10.5% demonstrated)
### Quantitative improvements (this work vs vLLM default):
- TTFT p50: **-60%** (0.731s vs 1.836s) from cache-aware session-sticky routing
- APC: **+24pp** (44.7% vs 20.8%)
- TPOT p90: **-15%** (0.073s vs 0.086s) from reduced prefill-decode interference via better cache utilization