What Makes an Inference Engine Actually Useful? 推理引擎怎样才算真正有用?
AI Infra Track Technology
This is Part 04 of my notes on learning AI infrastructure from zero. Earlier parts explained how a model computes and why serving gets hard. Here I use a Qwen-based coding agent to ask a more practical question: when does an inference optimization actually help a customer? The architecture diagrams are simplified, the example metrics are hypothetical, and the goal is to connect technical mechanisms to product decisions—not rank engines by an isolated benchmark.
00 Three complaints, three different engineering questions
Imagine a small startup building an AI coding agent on Qwen. Its product is working, but the team hears three complaints in the same week.
The temptation is to answer all three with one technical fix: more GPUs, larger batches, a faster engine. But those symptoms may come from different bottlenecks. This is where an inference engine becomes more than an engineering tool: its design choices become product choices.
The question is not “Which optimization is impressive?” but “Which bottleneck is limiting customer value?”
We'll use SGLang as a concrete system, compare it carefully with vLLM, and return to the coding agent to decide which knobs are actually worth turning.
01 Inside an inference engine: who does what?
Modules 01–03 explained prefill, decode, GPU bottlenecks and general serving. Here is how those pieces map to SGLang's serving runtime. The router is optional; a single-instance deployment can talk directly to the server.
The critical distinction is that the Scheduler decides what runs; the KV-cache subsystem tracks what can be reused and where it lives; and the ModelRunner / attention backend executes the remaining GPU work. An optional cluster router decides which instance receives the request, not the token-level execution order inside that instance.
For a coding agent, this separation determines whether a repeat visit can reuse repository context, whether a long prefill delays someone else's output, and whether memory pressure causes waiting or recovery.
02 RadixAttention: reuse is a memory-management problem
Imagine two requests that begin with the same 8,000-token repository context, then diverge: “fix authentication” and “add tests.” SGLang's radix tree indexes that shared prefix and references the corresponding KV slots. It is not the place where the full K/V tensors themselves live.
Under the right conditions, a second request can skip recomputing that shared portion of prefill. But 8,000 reused token positions do not imply an 8,000-token-equivalent reduction in latency or GPU cost. Queueing, decode, attention kernels, cache residency, and transfer overhead still matter.
Why reference counts and eviction matter
When active requests reference a shared prefix, those KV slots must not be reclaimed as ordinary idle cache. When requests release their references, eligible cache leaves can be evicted under memory pressure (often using LRU). SGLang separates prefix metadata, request-to-token mappings, and GPU KV storage so metadata can be reorganized without copying the full tensors. [2, 3]
For a coding product, the practical design rule is stable context first, changing context later—as long as this preserves instruction semantics, correct file versions, and tenant isolation. A changing timestamp or tool result at the front can break a long reusable prefix. Caching an outdated file is worse than a slower response.
03 Scheduling: maximizing occupancy can hurt the user
SGLang's Scheduler coordinates waiting requests, prefill batches, running decode batches and KV-memory allocation. Its policies can consider prefix reuse; its memory-aware paths may delay or retract requests when the next execution step cannot obtain enough slots. CPU–GPU overlap can hide some scheduling work behind GPU execution, but adds synchronization complexity. [2]
A batch policy that improves aggregate tokens per second can still worsen p95 time-to-first-token (TTFT) or time-between-output-tokens (ITL). That is not just a technical trade-off. For an interactive coding assistant, it can make the tool feel unreliable precisely during peak usage.
04 Disaggregated prefill and decode: where the handoff costs live
Instead of sharing one worker pool, SGLang can place prefill and decode on separate workers. This lets operators tune their capacity independently—but they now have to transfer the computed KV state before decoding can continue.
Prefill worker
Decode worker
One current architectural choice makes the trade-off concrete. With the early policy, decode can reserve destination KV slots before prefill finishes. With prefill_complete, allocation waits until prefill is ready. The latter can reduce time spent reserving decode memory, but may leave the prefill worker holding state longer and reduce transfer overlap; the documented option also has configuration restrictions. [4]
So the win is not “two worker types are always faster.” It is less interference and more controllable prefill/decode capacity when those benefits outweigh network and coordination cost. Short prompts, light traffic or slow inter-worker links may favor simpler unified serving.
05 SGLang vs. vLLM: compare the architecture, not a headline benchmark
Both projects support modern serving techniques, including continuous batching, prefix caching and multi-GPU execution. The more useful comparison is how they implement those ideas and how those choices behave under a specific workload.
| Design question | SGLang | vLLM (V1, documented design) |
|---|---|---|
| How is cached prefix found? | Radix tree: compressed token-prefix segments referencing KV slots. | Hash-based block lookup: a block hash incorporates its content and preceding prefix. |
| How is memory protected? | Cache-node references / locks, KV allocator, eviction rules. | Reference-counted KV blocks, free-block queue, block allocation / eviction. |
| What controls execution? | Scheduler, batch construction, prefix-aware policies and memory checks. | Token-budget scheduling and KV-cache manager working together. |
| Can prefill and decode separate? | Yes; disaggregation transfer backends include Mooncake and NIXL. | Yes; disaggregated prefilling is documented as experimental. |
| Which models / GPUs? | Version- and backend-dependent; check exact model and hardware matrix. | Also version- and backend-dependent; verify specific support. |
| Which is faster? | Depends on workload, configuration, model and hardware. | Depends on the same factors; no universal winner. |
Radix tree versus hash blocks is a data-structure distinction, not evidence that one always has a better cache-hit rate or TTFT. Both can reuse identical prefixes. Benchmarks also depend on software versions, request distributions, cache warmup, concurrency, output length and equal hardware budgets. [5, 6, 7]
06 The workload atlas: six users, six optimization hypotheses
This is the part I find most useful from a product perspective. “Inference is slow” is not a diagnosis. It is a place to begin asking better questions.
Below are six common coding-agent workload shapes. Each suggests different measurements and mechanisms. They are hypotheses, not automatic prescriptions.
Repeated repository context
- USER EXPERIENCE
- An agent asks follow-up questions over the same codebase.
- POSSIBLE BOTTLENECK
- Repeated prefill or low prefix reuse
- CHECK FIRST
- Cache-hit prefix length, TTFT, cache eviction
- CANDIDATE LEVER
- RadixAttention, stable prompt ordering
Different users, different prompts
- USER EXPERIENCE
- Many unrelated requests arrive with little shared context.
- POSSIBLE BOTTLENECK
- Low reuse; scheduling and model execution dominate
- CHECK FIRST
- Cache-hit distribution, queueing, TTFT/ITL
- CANDIDATE LEVER
- Load-aware scheduling, batching, better capacity allocation
Very long inputs
- USER EXPERIENCE
- A user supplies a 40k-token repository snapshot.
- POSSIBLE BOTTLENECK
- Heavy prefill and KV memory footprint
- CHECK FIRST
- Prefill time, peak KV usage, TTFT
- CANDIDATE LEVER
- Chunked prefill, prefix reuse, appropriate parallelism
Long generated outputs
- USER EXPERIENCE
- An agent produces a long refactor and explanation.
- POSSIBLE BOTTLENECK
- Decode steps, memory bandwidth, ongoing KV growth
- CHECK FIRST
- TPOT/ITL, output tokens/s, energy or cost
- CANDIDATE LEVER
- Decode batching, efficient attention kernels
Peak-hour concurrency
- USER EXPERIENCE
- It is fast at 9 a.m. and unpredictable at 3 p.m.
- POSSIBLE BOTTLENECK
- Queueing, admission limits, preemption, contention
- CHECK FIRST
- p95/p99 latency, queue time, retractions, goodput
- CANDIDATE LEVER
- Scheduler policies, admission control, capacity scaling
Dense, evolving agent context
- USER EXPERIENCE
- The agent keeps tool outputs, diffs and file history across turns.
- POSSIBLE BOTTLENECK
- Context growth, cache churn, stale context risk
- CHECK FIRST
- KV occupancy, prefix validity, task success, E2E time
- CANDIDATE LEVER
- Context design, selective retrieval, session-aware caching
Notice how the same knob can move from helpful to irrelevant across scenarios: prefix caching matters a lot when context repeats, but may offer little for unrelated prompts or long decode-heavy generations. Disaggregation may help noisy high-concurrency deployments while adding unnecessary transfer latency to small ones.
07 A product experiment: throughput up, useful work down
Let's say our startup tests three configurations on the same Qwen model, hardware budget, workload mix and SLO definition. A request counts as meeting the SLO only if it completes successfully within the agreed latency limits.
All numbers in this section are invented for teaching. They are not measured SGLang or vLLM performance.
| Configuration | Completed requests/s | SLO attainment | Goodput (req/s) |
|---|---|---|---|
| Current baseline | 20 | 65% | 13.0 |
| Aggressive batching | 24 | 50% | 12.0 |
| SLO-oriented scheduling | 19 | 90% | 17.1 |
The “aggressive batching” configuration finishes more requests in total, yet fewer requests meet the user-relevant requirement. Under this defined goal, the third configuration delivers more useful completions. It is a product win even without the highest raw throughput.
How I'd test it before shipping
08 Translate infrastructure into a customer promise
For a product-oriented reader, the strongest explanation of an infrastructure feature is a causal chain—not a list of kernel names.
Before proposing an optimization, I'd ask three questions:
Our workload
Do contexts repeat? Are prompts long, outputs long, or arrivals bursty?
Our bottleneck
Queueing, prefill, decode, memory pressure, transfer—or product correctness?
Our customers
Which SLO, reliability threshold, and cost per successful task actually matter?
References & boundaries
这是「AI Infra Learning Notes」的 Part 04。前几篇已经讨论过 LLM 如何计算、GPU 为什么成为瓶颈,以及 Serving System 如何处理大量请求。这次,我想把问题变得更贴近真实产品:为什么同样是“响应慢”,不同的 Coding Agent Workload 却需要完全不同的基础设施优化? 文中的架构图经过简化,性能数字均为教学假设。重点不在于宣布哪个引擎更快,而是把机制与产品决策真正联系起来。
00 三种用户抱怨,可能是三种完全不同的瓶颈
假设我们正在做一家使用 Qwen 的 AI Coding Agent startup。产品已经能用,但最近团队同时收到了三种反馈。
很容易想要用同一个办法回答这三个问题:买更大的 GPU、增加 batching,或者换一个“更快”的推理引擎。但这三种体验背后,可能是三种不同的 bottleneck。
真正的问题不是“哪项技术很厉害”,而是“什么瓶颈正在限制用户获得价值”。
这篇笔记以 SGLang 为一套真实的推理系统,比较它和 vLLM 的设计,再回到 Coding Agent 的产品场景,判断:什么值得优化,什么暂时不值得?
01 进入 Inference Engine:每个组件究竟负责什么?
我们在前几个 Module 已经理解了 prefill、decode、GPU bottleneck 和 serving 的通用原理。现在把这些知识映射到 SGLang 的实际运行结构。首先注意:Router 是可选组件,不是每一个单实例请求都必须经过它。
最关键的区分是:Scheduler 决定什么时候执行;KV Cache 子系统管理哪些计算结果可以复用、存在哪里;ModelRunner / Attention Backend 才执行剩余 GPU 计算。而可选的 Router 管的是把请求交给哪个实例,不是实例内部每一步 token 的调度。
对于 Coding Agent,这个分工决定了:重复打开同一代码库能否复用上下文、长 prefill 会不会干扰别人的生成、GPU memory 不够时请求是等待还是进入恢复流程。
02 RadixAttention:复用前缀,本质也是管理 GPU 内存
想象两个请求拥有完全相同的 8,000-token repository context,随后分别要求“修复登录问题”和“补充测试”。SGLang 使用 radix tree 索引公共前缀,并保存对应的 KV slot 引用。注意:树本身不是存储完整 K/V tensors 的地方。
满足条件时,第二个请求可以跳过已复用部分的 prefill。但复用 8,000 个 token positions,不代表 TTFT 或 GPU 成本按比例下降。排队、decode、attention kernels、缓存是否仍在、网络传输等都会影响最终结果。
Reference Count 与 Eviction 为什么重要?
活跃请求正在引用的 KV slots 不能被当作空闲缓存随便释放。请求结束、引用解除后,符合条件的 cache leaf nodes 才可能在内存紧张时被 eviction(常见策略是 LRU)。SGLang 分离 prefix metadata、request-to-token mappings 与 GPU KV storage,让系统能够调整索引,而无需为拆分树节点复制完整 tensors。[2, 3]
对应到 Coding Agent,一个有用的原则是:稳定上下文尽量放在前面,动态内容放在后面。但前提是不能改变指令语义、不能使用过时的文件版本,更不能破坏用户之间的数据隔离。为了更高 cache hit rate 而让 Agent 读错代码,是得不偿失的。
03 Scheduling:GPU 更忙,不代表用户体验更好
SGLang Scheduler 协调 waiting requests、prefill batches、正在运行的 decode requests 和 KV memory allocation。它可以考虑 prefix reuse;当下一轮执行缺少 KV slots 时,也可能推迟请求或进行 retraction / recovery。CPU–GPU overlap 能把部分 CPU 调度工作藏在 GPU 执行的时间里,但也增加同步复杂度。[2]
更激进的 batching 可能提高每秒生成的 tokens,却同时让 p95 TTFT 或 ITL 变差。这不是“技术指标之间的矛盾”这么简单:对交互式 Coding Agent 来说,用户会在高峰期直接感到这个工具不可靠。
04 Prefill–Decode Disaggregation:真正复杂的是交接
SGLang 可以将 Prefill 和 Decode 放在不同的 Worker 上,让两者的资源能够分别配置。但这也带来了一个新问题:Prefill 算出来的 KV state,必须安全、及时地传给 Decode Worker。
Prefill Worker
Decode Worker
一个很具体的架构选择是:使用 early allocation 时,Decode 可以在 Prefill 完成前预留 KV slots;改为 prefill_complete,就等 Prefill 准备好后再分配。后者可能减少 Decode Worker 提前占着内存等待的时间,但也可能让 Prefill Worker 更久地持有缓存,减少传输 overlap;该模式的官方文档还列出了具体的配置限制。[4]
因此,PD Disaggregation 并不等于“两类 Worker 一定更快”。它的价值在于:如果负载值得,系统可以减少 Prefill 对 Decode 的干扰,并独立调节两种资源。但网络、协调、运维成本可能抵消收益。短 prompt、低并发或较慢的 Worker 间网络,反而可能更适合 Unified Serving。
05 SGLang vs. vLLM:比较设计,而不是比较一张跑分图
这两套系统都支持成熟的 LLM serving 技术,例如 continuous batching、prefix caching 和多 GPU 执行。更有价值的问题是:它们用什么结构实现这些能力?在怎样的 workload 下,这些差异才会变得重要?
| 设计问题 | SGLang | vLLM(V1 的公开设计) |
|---|---|---|
| 如何查找缓存前缀? | Radix Tree:压缩的 token-prefix 节点,引用 KV slots。 | Hash-based blocks:利用 block 内容和此前 prefix 形成 hash 进行查找。 |
| 如何保护内存? | 节点引用 / lock、KV allocator、eviction rules。 | KV block reference count、free-block queue、allocation / eviction。 |
| 谁来决定执行? | Scheduler、batch construction、prefix-aware policies、memory checks。 | 统一 token-budget scheduling 与 KV Cache Manager 协作。 |
| 能否 Prefill / Decode 分离? | 可以;传输后端包括 Mooncake 与 NIXL。 | 可以;官方文档将 disaggregated prefilling 标为 experimental。 |
| 哪些模型 / GPU 可用? | 取决于版本和 backend,需要核对具体支持矩阵。 | 同样取决于版本和 backend,不能笼统判断。 |
| 谁一定更快? | 没有普遍胜者,要看具体测试条件。 | 同样没有普遍胜者。 |
Radix Tree 和 Hash Blocks 首先是数据结构的差别,不能直接推导出谁的 cache hit rate 或 TTFT 一定更好。两者都能复用相同的前缀。而 benchmark 还受到版本、输入输出分布、warm cache、并发、模型、GPU 和部署配置影响。[5, 6, 7]
06 Workload Atlas:六种用户场景,六种不同的优化假设
这是我觉得从 Product 角度最值得保留的一部分。“Inference 很慢”不是诊断结论,而是一个值得继续追问的现象。
下面整理了 六种常见 Coding Agent workload。每一种,都意味着不同的测量指标和工程机制。这些是有待检验的假设,而不是看到症状就能直接开出的处方。
重复的 Repository Context
- 用户看到的现象
- Coding Agent 多轮读取同一个代码库。
- 可能的瓶颈
- 重复 prefill,或 prefix reuse 不足
- 先测量什么
- 命中前缀长度、TTFT、cache eviction
- 可能的工程手段
- RadixAttention、稳定的 prompt 顺序
不同用户、不同 Prompts
- 用户看到的现象
- 大量请求几乎不共享上下文。
- 可能的瓶颈
- 缓存重用低,调度与模型执行更重要
- 先测量什么
- cache-hit 分布、排队时间、TTFT/ITL
- 可能的工程手段
- Load-aware scheduling、batching、容量配置
超长输入
- 用户看到的现象
- 用户一次传来 40k tokens 的仓库内容。
- 可能的瓶颈
- Prefill 计算重、KV memory 占用大
- 先测量什么
- prefill time、KV 峰值、TTFT
- 可能的工程手段
- Chunked prefill、prefix reuse、并行配置
超长输出
- 用户看到的现象
- Agent 要生成长篇重构方案和解释。
- 可能的瓶颈
- Decode 步骤多、带宽压力、KV 不断增长
- 先测量什么
- TPOT/ITL、output tokens/s、单位成本
- 可能的工程手段
- Decode batching、高效 attention kernels
高峰期高并发
- 用户看到的现象
- 上午很快,下午却时快时慢。
- 可能的瓶颈
- 排队、admission limit、preemption、资源竞争
- 先测量什么
- p95/p99 latency、queue time、retractions、goodput
- 可能的工程手段
- Scheduling、admission control、容量扩容
密集且不断变化的 Agent Context
- 用户看到的现象
- 工具结果、代码 diff、历史文件不断累计。
- 可能的瓶颈
- 上下文膨胀、cache churn、过期信息风险
- 先测量什么
- KV occupancy、prefix validity、任务成功率、端到端时间
- 可能的工程手段
- Context 组织、选择性检索、session-aware cache
这里有一个很重要的认识:同一个 optimization,在不同 workload 下可能从“特别有用”变成“几乎没用”。 比如 Prefix Caching 对重复上下文很有价值,却未必能帮助几乎不共享 prompt 的用户,或者主要耗时在长 decode 的任务。PD Disaggregation 也可能缓解高并发下的干扰,但对小流量服务反而增加额外传输开销。
07 一个产品实验:Throughput 提高,真正有用的工作反而减少
现在假设我们的 Startup 在同一个 Qwen 模型、相同 GPU 预算、相同 Workload Mix、相同 SLO 定义下,对比三种配置。只有请求成功完成、并满足预先约定的延迟要求,才被计为 SLO 达标。
以下全部是教学假设数据,不是 SGLang 或 vLLM 的真实 Benchmark。
| 配置 | 完成请求数 / 秒 | SLO 达标率 | Goodput(req/s) |
|---|---|---|---|
| 当前基线 | 20 | 65% | 13.0 |
| Aggressive Batching | 24 | 50% | 12.0 |
| SLO-oriented Scheduling | 19 | 90% | 17.1 |
Aggressive Batching 虽然让系统完成了更多请求,但真正达到用户要求的请求反而更少。在这个明确的目标下,第三种方案没有最高 Throughput,却完成了更多“有用的请求”。这才是产品意义上的优化。
如果由我来设计实验
08 把 Engineering Mechanism 翻译成真正的 Customer Value
对于做产品的人,我认为最有说服力的讲法不是罗列底层技术名称,而是建立一条完整的因果链。
在建议团队动手优化之前,我想先问三个问题:
Our Workload
我们的 Context 重复吗?是长 Prompt、长 Output,还是请求突然集中涌入?
Our Bottleneck
问题发生在 Queue、Prefill、Decode、KV Memory、Transfer,还是 Agent 本身的正确性?
Our Customers
用户真正看重什么 SLO、可靠性、任务成功率与单位成本?