Download scripts/decode_latency_serving.py from poolside-laguna-hackathon/kv-landlords: direct link, hf CLI and curl.
- Browser
- Download file 4.26 kB
-
https://huggingface.co/poolside-laguna-hackathon/kv-landlords/resolve/main/scripts/decode_latency_serving.py
- Command line
-
hf download hf://poolside-laguna-hackathon/kv-landlords/scripts/decode_latency_serving.py
-
curl -L -o decode_latency_serving.py https://huggingface.co/poolside-laguna-hackathon/kv-landlords/resolve/main/scripts/decode_latency_serving.py
4.26 kB
| """Single-stream DECODE LATENCY on the vLLM serving path (the latency regime). | |
| The batched HumanEval bench (`longctx_code_serving.py`) runs all prompts at once, | |
| so decode happens at large batch — the regime where the fused INT4 read is still | |
| ~14-17x of FlashAttention and the bf16-tensor-core win is smallest. This script | |
| measures the OTHER regime: one request at a time (`max_num_seqs=1`), long context, | |
| pure-decode steps. That is where the batch-1 kernel speedup must show up if it is | |
| worth anything, and it is a real serving regime (interactive / low-concurrency | |
| single-stream latency). | |
| We report median per-output-token decode latency (ms/tok) and tokens/s over R | |
| requests of a fixed long prefix + fixed decode length. Run once per dtype: | |
| KVD=auto -> bf16 KV cache (ceiling) | |
| KVD=int4_kivi -> our INT4-KIVI backend (swap the OLD kernel in to A/B) | |
| Env: KVD, PREFIX_TOKENS (12000), MAXNEW (256), R (8 requests), MODEL. | |
| """ | |
| import glob | |
| import json | |
| import os | |
| import statistics | |
| import time | |
| from vllm import LLM, SamplingParams | |
| ROOT = "/home/alex/poolside-hackathon-kv-quant" | |
| MODEL = os.environ.get("MODEL", "poolside/Laguna-XS.2") | |
| KVD = os.environ.get("KVD", "int4_kivi") | |
| PREFIX_TOKENS = int(os.environ.get("PREFIX_TOKENS", "12000")) | |
| MAXNEW = int(os.environ.get("MAXNEW", "256")) | |
| R = int(os.environ.get("R", "8")) | |
| def build_prefix_text(tok, target_tokens: int): | |
| files = [] | |
| for venv in (".venv-vllm", ".venv"): | |
| files = sorted(glob.glob(f"{ROOT}/{venv}/**/transformers/**/modeling_*.py", | |
| recursive=True)) | |
| if files: | |
| break | |
| if not files: | |
| files = sorted(glob.glob(f"{ROOT}/**/*.py", recursive=True)) | |
| texts = [] | |
| for f in files: | |
| try: | |
| texts.append(open(f).read()) | |
| except OSError: | |
| continue | |
| ids = tok("\n\n".join(texts))["input_ids"] | |
| if len(ids) >= target_tokens: | |
| return tok.decode(ids[:target_tokens]), target_tokens | |
| ids = tok("\n\n".join(texts))["input_ids"] | |
| return tok.decode(ids), len(ids) | |
| # max_num_seqs=1 -> the engine can only ever run ONE sequence at a time, so every | |
| # decode step is batch-1: the latency regime the batch-1 kernel win targets. | |
| llm = LLM(model=MODEL, dtype="bfloat16", kv_cache_dtype=KVD, | |
| gpu_memory_utilization=0.55, max_model_len=PREFIX_TOKENS + 2048, | |
| max_num_seqs=1, enforce_eager=True) | |
| tok = llm.get_tokenizer() | |
| prefix_text, prefix_tok = build_prefix_text(tok, PREFIX_TOKENS) | |
| # Fixed decode length so latency is comparable across dtypes: ignore EOS, force | |
| # exactly MAXNEW tokens. Greedy. | |
| sp = SamplingParams(temperature=0.0, max_tokens=MAXNEW, min_tokens=MAXNEW, | |
| ignore_eos=True) | |
| base_msgs = [ | |
| {"role": "system", "content": "You are a Python coding assistant."}, | |
| {"role": "user", "content": | |
| "Here is some reference Python source code for context.\n\n" | |
| f"```python\n{prefix_text}\n```\n\n" | |
| "Now write a Python function `solve(n)` that returns the n-th Fibonacci " | |
| "number, with a docstring and an iterative implementation."}, | |
| ] | |
| # Warmup (compile kernels / JIT) — not timed. | |
| llm.chat([base_msgs], sp, add_generation_prompt=True) | |
| per_req_ms_tok = [] | |
| per_req_gen_s = [] | |
| out_tokens = [] | |
| for r in range(R): | |
| t0 = time.time() | |
| outs = llm.chat([base_msgs], sp, add_generation_prompt=True) | |
| dt = time.time() - t0 | |
| n_out = len(outs[0].outputs[0].token_ids) | |
| per_req_gen_s.append(dt) | |
| out_tokens.append(n_out) | |
| per_req_ms_tok.append(dt / max(n_out, 1) * 1e3) | |
| med_ms = statistics.median(per_req_ms_tok) | |
| med_s = statistics.median(per_req_gen_s) | |
| tok_s = 1000.0 / med_ms | |
| print(f"=== [{KVD}] single-stream decode latency (max_num_seqs=1) ===") | |
| print(f" prefix={prefix_tok} tok decode={MAXNEW} tok R={R} requests") | |
| print(f" median {med_ms:.3f} ms/tok {tok_s:.1f} tok/s " | |
| f"req {med_s:.2f}s (out tok {min(out_tokens)}..{max(out_tokens)})") | |
| json.dump({"kvd": KVD, "prefix_tok": prefix_tok, "maxnew": MAXNEW, "R": R, | |
| "median_ms_per_tok": med_ms, "tok_per_s": tok_s, | |
| "median_req_s": med_s, "per_req_ms_tok": per_req_ms_tok}, | |
| open(f"/tmp/decode_latency_{KVD}.json", "w")) | |
| print(f"DECODE_LATENCY_SERVING DONE [{KVD}]") | |