dbf85683e6
KV cache compression for LLM inference (ICLR 2026, arXiv:2504.19874). Core: - TurboQuantProd: 3-bit keys (MSE + QJL), 2-bit/4-bit values (group quant) - Modular architecture: capture, store, score, integration/vllm - vLLM monkey-patch with free_kv_cache and hybrid decode - 3 fused Triton kernels for decode attention Validated on: - RTX 5090: Qwen3.5-27B-AWQ, 30GB KV freed, 2x context capacity - 8x RTX 3090: Qwen3.5-35B-A3B MoE at 131k context - 8,238 tok/s prefill, 98 tok/s decode, 15.9s TTFT - 30.9% KV savings (4.4x on full-attn layers, 1.45x overall) - 5/5 needle retrieval at max context 35 tests pass (19 modular + 7 core + 9 paper validation). Adversarial audit included with honest assessment of all claims.
295 lines
14 KiB
HTML
295 lines
14 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>TurboQuant: Paper vs Implementation</title>
|
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
<style>
|
|
:root {
|
|
--bg: #0d1117; --surface: #161b22; --raised: #1c2129;
|
|
--border: #30363d; --text: #e6edf3; --muted: #8b949e;
|
|
--accent: #58a6ff; --green: #3fb950; --red: #f85149; --yellow: #d29922;
|
|
--green-bg: rgba(63,185,80,0.10); --red-bg: rgba(248,81,73,0.10); --yellow-bg: rgba(210,153,34,0.10);
|
|
}
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--bg); color: var(--text); line-height: 1.55; }
|
|
.hdr { border-bottom: 1px solid var(--border); padding: 40px 24px 32px; text-align: center; }
|
|
.hdr h1 { font-size: 28px; font-weight: 700; letter-spacing: -0.4px; }
|
|
.hdr h1 span { color: var(--accent); }
|
|
.hdr .sub { color: var(--muted); font-size: 14px; margin-top: 6px; }
|
|
.wrap { max-width: 960px; margin: 0 auto; padding: 24px 20px 48px; }
|
|
.s { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 24px; margin-bottom: 20px; }
|
|
.s h2 { font-size: 19px; font-weight: 700; margin-bottom: 16px; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
|
|
h3 { font-size: 15px; font-weight: 600; margin: 16px 0 8px; }
|
|
p, li { font-size: 14px; } p { margin-bottom: 10px; }
|
|
ul, ol { padding-left: 18px; margin-bottom: 10px; } li { margin-bottom: 3px; }
|
|
strong { color: #fff; }
|
|
code { font-family: 'SF Mono', monospace; font-size: 12px; background: var(--raised); padding: 1px 5px; border-radius: 3px; border: 1px solid var(--border); }
|
|
table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 13px; }
|
|
th { text-align: left; padding: 8px 10px; background: var(--raised); border: 1px solid var(--border); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: 0.3px; color: var(--muted); }
|
|
td { padding: 8px 10px; border: 1px solid var(--border); }
|
|
.st { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 6px; border-radius: 3px; }
|
|
.ok { background: var(--green-bg); color: var(--green); }
|
|
.warn { background: var(--yellow-bg); color: var(--yellow); }
|
|
.fail { background: var(--red-bg); color: var(--red); }
|
|
.badge { display: inline-block; font-size: 10px; font-weight: 600; text-transform: uppercase; padding: 2px 6px; border-radius: 4px; margin-left: 6px; vertical-align: middle; }
|
|
.mw { background: #fff; border-radius: 6px; padding: 16px 12px; margin: 12px 0; overflow-x: auto; }
|
|
.g2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 12px 0; }
|
|
.g2 .c { background: var(--raised); border: 1px solid var(--border); border-radius: 6px; padding: 16px; }
|
|
.c h3 { margin-top: 0; }
|
|
.c ul { margin-bottom: 0; }
|
|
.cw { border-left: 3px solid; padding: 10px 14px; border-radius: 0 6px 6px 0; margin: 12px 0; font-size: 13px; }
|
|
.cw-r { border-color: var(--red); background: var(--red-bg); }
|
|
@media (max-width: 640px) { .g2 { grid-template-columns: 1fr; } .hdr h1 { font-size: 22px; } .s { padding: 16px; } }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="hdr">
|
|
<h1><span>TurboQuant</span>: Paper vs Implementation</h1>
|
|
<p class="sub">arXiv:2504.19874 (ICLR 2026) — Qwen3.5-27B-AWQ on RTX 5090 — <a href="https://github.com/0xSero/turboquant" style="color:var(--accent)">github.com/0xSero/turboquant</a></p>
|
|
</div>
|
|
<div class="wrap">
|
|
|
|
<!-- A: Implementation Status -->
|
|
<div class="s">
|
|
<h2>Implementation Status vs Paper</h2>
|
|
<table>
|
|
<tr><th>Paper Component</th><th>Status</th><th>File</th></tr>
|
|
<tr><td>Random orthogonal rotation (QR)</td><td><span class="st ok">Faithful</span></td><td><code>rotation.py</code></td></tr>
|
|
<tr><td>Lloyd-Max codebook on Beta PDF</td><td><span class="st ok">Faithful</span></td><td><code>codebook.py</code></td></tr>
|
|
<tr><td>TurboQuant_MSE (Algorithm 1)</td><td><span class="st ok">Faithful</span></td><td><code>quantizer.py</code></td></tr>
|
|
<tr><td>TurboQuant_Prod (Algorithm 2)</td><td><span class="st ok">Faithful</span></td><td><code>quantizer.py</code></td></tr>
|
|
<tr><td>QJL projection + sign packing</td><td><span class="st ok">Faithful</span></td><td><code>quantizer.py</code>, <code>rotation.py</code></td></tr>
|
|
<tr><td>Value quantization</td><td><span class="st warn">Not from paper</span></td><td><code>kv_cache.py</code> (group quant)</td></tr>
|
|
<tr><td>Outlier channel splitting</td><td><span class="st fail">Removed</span></td><td>Was dead code, stripped</td></tr>
|
|
<tr><td>MLA support</td><td><span class="st fail">Stub</span></td><td><code>integration/vllm.py</code></td></tr>
|
|
<tr><td>vLLM integration + Triton kernels</td><td><span class="st ok">Novel eng.</span></td><td><code>integration/</code>, <code>triton_kernels.py</code></td></tr>
|
|
<tr><td>Modular capture/store/score</td><td><span class="st ok">Novel eng.</span></td><td><code>capture.py</code>, <code>store.py</code>, <code>score.py</code></td></tr>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- B: Attention Backends -->
|
|
<div class="s">
|
|
<h2>Attention Backends</h2>
|
|
<table>
|
|
<tr><th>Backend</th><th>Status</th><th>Notes</th></tr>
|
|
<tr><td>Flash Attention (MHA/GQA)</td><td><span class="st ok">Working</span></td><td>Primary path, hooks on FlashAttentionImpl</td></tr>
|
|
<tr><td>Flash Attention (no-alloc prefill)</td><td><span class="st warn">Degraded</span></td><td>Falls back to F.scaled_dot_product_attention</td></tr>
|
|
<tr><td>FlashInfer</td><td><span class="st fail">Incompatible</span></td><td>Metadata mismatch with FP8 KV</td></tr>
|
|
<tr><td>MLA</td><td><span class="st fail">Stub</span></td><td>Passthrough only</td></tr>
|
|
<tr><td>GDN / Mamba</td><td><span class="st warn">N/A</span></td><td>No KV cache, TQ hooks on attention layers only</td></tr>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- C: Known Issues -->
|
|
<div class="s">
|
|
<h2>Known Issues <span class="badge" style="background:var(--red-bg);color:var(--red);">After Trim</span></h2>
|
|
<ul>
|
|
<li><strong>No-alloc decode fallback returns zeros</strong> when TQ store has <16 tokens</li>
|
|
<li><strong>No-alloc prefill uses <code>repeat_interleave</code></strong> for GQA, massive memory at 200k</li>
|
|
<li><strong>Value group_size hardcoded to 32</strong> (now explicit, was previously fragile math)</li>
|
|
<li><strong>MLA path is passthrough</strong> — no quantization for DeepSeek V3 etc.</li>
|
|
<li><strong>200k baseline stalls</strong> — no dual-case telemetry available</li>
|
|
<li><strong>Quality benchmarks are synthetic</strong> — no LongBench/RULER runs</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- D: Validation Results -->
|
|
<div class="s">
|
|
<h2>Validation Results (validate_paper.py) <span class="badge" style="background:var(--green-bg);color:var(--green);">9/9 pass</span></h2>
|
|
<table>
|
|
<tr><th>Test</th><th>What it validates</th><th>Result</th></tr>
|
|
<tr><td>MSE distortion bounds</td><td>Theorem 1: MSE ≤ √3π/2 · 1/4<sup>b</sup></td><td><span class="st ok">Pass</span> (b=1..4)</td></tr>
|
|
<tr><td>Codebook Table 1</td><td>Lloyd-Max MSE matches paper values</td><td><span class="st ok">Pass</span></td></tr>
|
|
<tr><td>Unbiasedness</td><td>Theorem 2: E[⟨y, ˜x⟩] = ⟨y, x⟩</td><td><span class="st ok">Pass</span> (bits=2,3,4)</td></tr>
|
|
<tr><td>Distortion scaling</td><td>Theorem 3: 1/4<sup>b</sup> scaling</td><td><span class="st ok">Pass</span> (ratios >2x per bit)</td></tr>
|
|
<tr><td>Recall@8 (N=4096)</td><td>Attention ranking quality at scale</td><td><span class="st ok">Pass</span></td></tr>
|
|
<tr><td>Rank correlation (N=2048)</td><td>Spearman corr 3-bit >0.75, 4-bit >0.90</td><td><span class="st ok">Pass</span></td></tr>
|
|
<tr><td>Needle@5 depths (N=4096)</td><td>Retrieval at 10/25/50/75/90% depth</td><td><span class="st ok">Pass</span> (3+4 bit)</td></tr>
|
|
<tr><td>Needle chunked (N=8192)</td><td>Multi-chunk retrieval</td><td><span class="st ok">Pass</span></td></tr>
|
|
<tr><td>Compression ratio</td><td>>2x vs FP16</td><td><span class="st ok">Pass</span> (5.1x)</td></tr>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- E: What was stripped -->
|
|
<div class="s">
|
|
<h2>What Was Stripped</h2>
|
|
<div class="g2">
|
|
<div class="c">
|
|
<h3 style="color:var(--red);">Removed</h3>
|
|
<ul>
|
|
<li><code>n_outlier_channels</code> / <code>outlier_key_bits</code> params (dead code)</li>
|
|
<li><code>_effective_bits()</code> method (never called)</li>
|
|
<li><code>_expand_query_for_gqa()</code> (dead function)</li>
|
|
<li><code>try_fused_decode()</code> (never called from working paths)</li>
|
|
<li>750+ lines of legacy classes in <code>vllm_attn_backend.py</code></li>
|
|
<li>Dead <code>console_scripts</code> entry point in <code>setup.py</code></li>
|
|
<li>Unused imports (<code>F</code>, <code>Tuple</code>, <code>MSEQuantized</code>, etc.)</li>
|
|
</ul>
|
|
</div>
|
|
<div class="c">
|
|
<h3 style="color:var(--green);">Fixed</h3>
|
|
<ul>
|
|
<li>Missing <code>import os</code> in <code>test_triton_kernels.py</code></li>
|
|
<li>Fragile group_size calc in <code>score.py</code> → explicit <code>32</code></li>
|
|
<li><code>vllm_attn_backend.py</code> reduced from 756 to ~190 lines (thin shim)</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Diagram 1: System Architecture -->
|
|
<div class="s">
|
|
<h2>Architecture</h2>
|
|
<div class="mw">
|
|
<pre class="mermaid">
|
|
graph TB
|
|
subgraph "Core Quantization"
|
|
CB["codebook.py<br/>Lloyd-Max on Beta PDF"]
|
|
ROT["rotation.py<br/>QR rotation + QJL matrix"]
|
|
QMSE["TurboQuantMSE<br/>Rotate -> Quantize -> Pack"]
|
|
QPROD["TurboQuantProd<br/>MSE@(b-1) + QJL residual"]
|
|
end
|
|
subgraph "Serving Stack"
|
|
CAP["capture.py<br/>RingBuffer + KVCaptureEngine"]
|
|
STORE["store.py<br/>CompressedKVStore"]
|
|
SCORE["score.py<br/>compute_hybrid_attention"]
|
|
end
|
|
subgraph "vLLM Integration"
|
|
VLLM["integration/vllm.py<br/>off | capture_only | hybrid"]
|
|
SHIM["vllm_attn_backend.py<br/>enable_no_alloc shim"]
|
|
end
|
|
CB --> QMSE
|
|
ROT --> QMSE
|
|
ROT --> QPROD
|
|
QMSE --> QPROD
|
|
QPROD --> STORE
|
|
CAP --> STORE
|
|
STORE --> SCORE
|
|
VLLM --> CAP
|
|
VLLM --> SCORE
|
|
SHIM --> VLLM
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Diagram 2: Quantization Pipeline -->
|
|
<div class="s">
|
|
<h2>Quantization Pipeline</h2>
|
|
<div class="mw">
|
|
<pre class="mermaid">
|
|
graph LR
|
|
subgraph "Key: TurboQuant_Prod"
|
|
K["Key (H, T, D)"] --> NORM["||x||, normalize"]
|
|
NORM --> ROT["y = x @ Pi_T"]
|
|
ROT --> IDX["searchsorted -> indices"]
|
|
IDX --> PACK["bit-pack (b-1 bits)"]
|
|
ROT --> DQ["dequant MSE"]
|
|
DQ --> RES["r = x - x_mse"]
|
|
RES --> QJL["sign(r @ S_T)"]
|
|
QJL --> SIGNS["pack signs (1 bit/coord)"]
|
|
RES --> RNORM["||r||"]
|
|
end
|
|
subgraph "Value: Group Quant"
|
|
V["Value (H, T, D)"] --> GRP["reshape to groups"]
|
|
GRP --> MM["per-group min/max"]
|
|
MM --> VQ["round + bit-pack (2-bit)"]
|
|
end
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Diagram 3: Read/Write Paths -->
|
|
<div class="s">
|
|
<h2>Read / Write Paths</h2>
|
|
<div class="mw">
|
|
<pre class="mermaid">
|
|
graph TB
|
|
subgraph "Write"
|
|
PRE["Prefill N tokens"] --> SP{"N > ring?"}
|
|
SP -->|Yes| CMP["Compress first N-ring -> store"]
|
|
SP -->|Yes| BUF["Last ring tokens -> ring buffer"]
|
|
SP -->|No| BA["All -> ring buffer"]
|
|
DEC["Decode 1 token"] --> RW["ring.write"]
|
|
RW --> OV{"Overflow?"}
|
|
OV -->|Yes| FL["Drain -> store.append_chunk"]
|
|
end
|
|
subgraph "Read (Decode)"
|
|
Q["Query"] --> HH{"History >= 16?"}
|
|
HH -->|No| EX["Attend exact buffer only"]
|
|
HH -->|Yes| HY["Hybrid: dequant history + cat recent"]
|
|
HY --> ATT["einsum attention with GQA broadcast"]
|
|
end
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Diagram 4: vLLM Hook Chain -->
|
|
<div class="s">
|
|
<h2>vLLM Hook Chain</h2>
|
|
<div class="mw">
|
|
<pre class="mermaid">
|
|
sequenceDiagram
|
|
participant U as User
|
|
participant E as enable_no_alloc
|
|
participant X as Executor
|
|
participant I as FlashAttentionImpl
|
|
participant T as TQ LayerState
|
|
U->>E: enable_no_alloc(bits=3)
|
|
E->>X: patch get_kv_cache_specs
|
|
U->>X: LLM() init
|
|
X->>I: install_hooks (16 layers)
|
|
I->>T: create store + engine per layer
|
|
Note over I: Share KV cache across TQ layers
|
|
U->>X: generate(prompt)
|
|
Note over I: PREFILL
|
|
I->>T: SDPA attention + capture K/V
|
|
Note over I: DECODE
|
|
I->>T: compute_hybrid_attention
|
|
T-->>I: output
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Summary -->
|
|
<div class="s">
|
|
<h2>Summary</h2>
|
|
<div class="g2">
|
|
<div class="c">
|
|
<h3 style="color:var(--green);">What Works</h3>
|
|
<ul>
|
|
<li>Core TQ_MSE + TQ_Prod faithful to paper</li>
|
|
<li>All 3 theorems validated empirically</li>
|
|
<li>Needle retrieval passes at 8192 tokens</li>
|
|
<li>5.1x compression vs FP16</li>
|
|
<li>30k A/B: +5.7% prefill, +3.1% output speed</li>
|
|
<li>200k TQ completed (199,952 tokens)</li>
|
|
</ul>
|
|
</div>
|
|
<div class="c">
|
|
<h3 style="color:var(--red);">What Doesn't</h3>
|
|
<ul>
|
|
<li>MLA: stub only (DeepSeek V3 unsupported)</li>
|
|
<li>FlashInfer: incompatible</li>
|
|
<li>200k quality unvalidated (no LongBench)</li>
|
|
<li>Multi-sequence batching untested</li>
|
|
<li>No-alloc prefill slow (SDPA, not flash)</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="cw cw-r">
|
|
<strong>Key gap:</strong> The "identical output" claim holds only for trivial prompts. Long-context retrieval quality is unquantified against standard benchmarks (LongBench, RULER, Needle-in-Haystack at 200k). The 200k needle failure in the handoff doc is concerning.
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<script>
|
|
mermaid.initialize({ startOnLoad: true, theme: 'default', securityLevel: 'loose',
|
|
flowchart: { useMaxWidth: true, curve: 'basis' },
|
|
sequence: { useMaxWidth: true, wrap: true }
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|