vLLM Unified Architecture Report
一份文档,合并了此前所有报告:
spine + L1–L4 agent + 第一性原理(软件+物理双视角)
+ Performance Handbook + Ascend 官方设计文档 + community 90 天 pulse。
每个 section 顶部标注 source: 溯源锚点。全部原始报告保存在
/vllm_research/vllm_analysis_raw/,1:1 可回溯。
阅读顺序: §0 Overview → §1 软件架构第一性原理 → §2 分层 → §3 热点路径 → §4–§10 深度 → §11 引用。
0. Overview & v0→v1 Synthesis
This single document merges all prior analyses (spine, L1–L4 agents,
first-principles software+physical, performance handbook, community
pulse, Ascend official design docs) into one navigable report without
losing any file:line source anchor. All original raw reports are
preserved in /vllm_research/vllm_analysis_raw/ for 1:1
traceability. Each section carries a source band (source: …).
Reading order: this overview → §1 first-principles → §2 architecture
layers → §3 execution hot path → §4–§10 deep dives → §11 references.
0.1 v0 的两条缠结 → v1 的解法
| v0 症状 | 根因 (v0 结构) | v1 解法 |
|---|---|---|
| 调度策略改动必须同时改 Worker+Scheduler+ModelRunner | 三者按“功能”分区,没按“变化速率”分区 | Scheduler 独立 + EngineCore 单线程事件循环 |
| 长 prefill 卡死 decode,TTFT/TTIT 不可同时优化 | prefill phase + decode phase 两阶段模型 | 单一 token-budget 循环,无阶段之分 |
| 加新硬件需碰 200+ 个模型文件 | attention kernel 嵌在模型代码里 | AttentionBackend + Builder + Impl + KVCacheSpec 家族 |
| prefix cache / CUDA graph / offload 改动互相传染 | 单一 BlockSpaceManager 耦合请求生命周期+分配+缓存+swap | 拆 KV-Stack 为 Manager/Coordinator/BlockPool/Connector 四层 |
1. Software-Architecture First Principles
1.1 The axiom applied — rates → modules
| Rate | vLLM code element | Isolated into module |
|---|---|---|
| daily — policy, schedule order, threshold | recompute / SRF / chunked / FCFS / profiling-chunk | Scheduler |
| weekly — new models | 200+ models (DeepSeek/Qwen/GLM/Gemma/Kimi/MiniMax…) | models/ directory |
| monthly — new HW / new kernel variants | FA3 / FlashInfer / Triton / CANN / ROCm | AttentionBackend + ops/ |
| quarterly — topology, comm layout | TP/PP/DP/EP/CP combinations | Worker + distributed/ |
| yearly — execution paradigm | CUDA Graph / torch.compile / ACL Graph | Compilation |
| never — inference math | "1 step = schedule → forward → sample" | EngineCore.step |
1.2 Each module — its ONE job and what it never touches
| Module | Owns | Does NOT touch |
|---|---|---|
EngineCorev1/engine/core.py:101 | Time-line (the step() loop), request lifecycle, pause/resume/sleep/wake, DP coordination, LoRA hot-plug | device, model, tensor, scheduling strategy |
Schedulerv1/core/sched/scheduler.py:418 | PURE decision fn: request state + blocks -> SchedulerOutput | device, model, kernels, comm |
Workerv1/worker/worker_base.py+gpu_worker.py | The device, weights in HBM, KV-cache tensors, load/determine_memory/sleep/health | scheduling decisions, request state |
ModelRunnerv1/worker/gpu/model_runner.py:1134 | Translates SchedulerOutput -> kernel dispatch (5 stages) | request admission, scheduling, device discovery |
AttentionBackendv1/attention/backend.py | Common attn metadata -> HW-private metadata | model structure, scheduling, KV-cache ownership |
| KV 4-layer stack | Manager (request↔block) / Coordinator (multi-group) / BlockPool (free-list+hash) / Connector (cross-worker tiering) | scheduling dispatch, model execution |
Compilationcompilation/ | Kernel launches -> graph replay (FULL/PIEWISE/NONE) | model semantics, scheduling |
Platformplatforms/ | Device-specific assumptions behind 6 mandatory methods | everything above |
1.3 Cross-module contracts — why they are cut this way
| Contract | Producer → Consumer | Design decision | Why |
|---|---|---|---|
SchedulerOutputv1/core/sched/output.py | Scheduler → ModelRunner | only decisions (block ids + token counts), no tensors | Scheduler runs CPU-only; enables cross-node transport (PD disagg) |
ForwardContextforward_context.py:132 | ModelRunner → model | model reads ForwardContext only; never sees SchedulerOutput | changing scheduling/graph-mode/parallelism never touches 200+ model files |
| block_tables + slot_mappings | ModelRunner → AttentionBackend | ONE translation shared by N backends | avoids N copies of page-table-walk logic |
KVCacheBlockskv_cache_manager.py:30 | Scheduler → Worker | only block ids, no tensors | cross-node transport (PD disagg) |
| allocate_slots → None | KVCacheManager → Scheduler | returns None instead of raising on OOM | "allocation failure" → normal control flow; one while-loop handles both allocate & preempt |
1.4 First-principles meta-judgement — forced vs chosen
Among all vLLM's layers, a few are forced by physics: paged-KV, Attention-as-separable-backend, Communication-via-Connector, Platform-as-interface — these are stable across HW generations. Others are engineering choices a v2 may freely redesign: the Python front-end, the shm-RPC executor, capability-dispatched sampling, the patch layer. Every heavily-patched subsystem is a gap in vLLM's abstraction surface.
2. Architecture Layers
2.1 EngineCore — the time-line owner
v1/engine/core.py:101 EngineCore. The heartbeat (core.py:576):
def step(self):
scheduler_output = self.scheduler.schedule() # 1. decide
future = self.executor.execute_model(scheduler_output, non_block=True) # 2. async dispatch
model_output = future.result() # 3. wait
engine_core_outputs = self.scheduler.update_from_output(scheduler_output, model_output)
EngineCore also owns: add/abort, pause/resume, sleep/wake, LoRA hot-plug, cache reset (reset_prefix_cache/reset_encoder_cache), DP coordination (step_with_batch_queue), spec-draft collection (post_step → take_draft_token_ids). It holds NO device/model/tensor — runs CPU-only, the ultimate control/data-plane split.
2.2 Scheduler — the pure decision function
v1/core/sched/scheduler.py:418. The design axiom (scheduler.py:420):
# There's no "decoding phase" nor "prefill phase" in the scheduler.
# Each request just has num_computed_tokens and num_tokens_with_spec …
# At each step, the scheduler tries to assign tokens to the requests so
# that each request's num_computed_tokens can catch up its
# num_tokens_with_spec. This is general enough to cover chunked prefills,
# prefix caching, speculative decoding, and "jump decoding."
Unified token-budget loop over RUNNING then WAITING, chunked via long_prefill_token_threshold, preemption when allocate_slots returns None. Holds no device/model/comm.
2.3 Worker — the device owner
v1/worker/worker_base.py+gpu_worker.py. One Worker per GPU/NPU. Responsibilities: init_device, load_model/reload_weights, determine_available_memory, kv_caches tensors, sleep/wake, LoRA add/remove/pin, execute_model, sample_tokens, health check. Does NOT hold Scheduler, makes no scheduling decisions, holds no request state — consumes SchedulerOutput statelessly. Worker holds ModelRunner but the two are separate: same device can run different models; same model can run on different devices (Ascend NPUWorker(NPUModelRunner) subclasses but keeps the interface).
2.4 ModelRunner — the forward executor
| # | stage | input → output | meaning |
|---|---|---|---|
| 1 | Request bookkeeping | SchedulerOutput → req_states | decisions → local state |
| 2 | prepare_inputs | req_states → InputBatch{input_ids, positions, q_start_loc, seq_lens, logits_indices} | request state → model input tensors |
| 3 | prepare_attn | req_states+pool → block_tables[groups,reqs,blocks] + slot_mappings[groups,tokens] | block ids → page-table walk |
| 4 | model_state.prepare_attn | common_attn_metadata → attn_metadata[per-backend] | common → HW-private metadata |
| 5 | model forward / cudagraph replay | inputs → hidden/logits | tensors → compute |
2.5 AttentionBackend — the only HW-sensitive op
Attention is the ONLY op whose memory-access pattern depends on sequence length + KV layout (GEMM/Norm/MoE/RoPE are all regular (N,d)→(N,d)). The triple: register_backend + Builder.build(common) + Impl.forward + KVCacheSpec family (FullAttentionSpec / MLASpec / RSWASpec / RaggedSpec). ModelRunner only sees KVCacheSpec, not concrete model — this decouples "model structure change" from "KV management".
2.6 Platform — device as interface
platforms/. 6 mandatory methods. Holds device capability/memory/stream/attn-backend-selection/graph-pool. Ascend NPUPlatform overrides only identity+backend-map+memory+compile hooks; its get_device_total_memory raises NotImplementedError (torch_npu global-init-only) — a canonical "interface promise vs device capability" mismatch.
2.7 Compilation — "record once, replay many"
compilation/+v1/worker/gpu/cudagraph_utils.py. Platform injects ACLGraphWrapper via get_static_graph_wrapper_cls(). See §6 for the 3-mode trade-off and ACL-specific constraints.
3. Execution Hot Path (Scheduler → ModelRunner → Sample)
3.1 The page-table walk (slot_mapping kernel)
v1/worker/gpu/block_table.py:283 (_compute_slot_mappings_kernel)
block_indices = positions // (block_size * CP_SIZE)
block_offsets = positions % (block_size * CP_SIZE)
block_numbers = tl.load(block_table_ptr + req_state_idx*stride + block_indices)
slot_ids = block_numbers * block_size + block_offsets <- physical address
phys = ppn * PAGE_SIZE + page_offset: block_table=page table, block_id=PFN, slot_id=linear address. PAD_SLOT_ID = "invalid PTE". vLLM doing virtual-memory management on GPU HBM, formalised.3.2 The 3-line heartbeat
def step(self):
scheduler_output = self.scheduler.schedule()
future = self.executor.execute_model(scheduler_output, non_block=True)
engine_core_outputs = self.scheduler.update_from_output(scheduler_output, future.result())
3.3 SchedulerOutput — the Scheduler↔Worker contract
v1/core/sched/output.py: scheduled_new/resumed/running/preempted reqs, req_to_new_blocks, num_scheduled_tokens, scheduled_spec_decode_tokens, scheduled_encoder_inputs, finished_req_ids. Carries ONLY decisions (block ids + token counts) — no tensors — so it crosses process/node boundaries (PD disagg relies on this). 4 explicit categories (new/resumed/running/preempted) let Worker use simple if-else. Block allocation done in Scheduler side — Worker only consumes block ids.
3.4 ForwardContext — the model↔framework firewall
forward_context.py:132: attn_metadata[per-layer], slot_mapping[per-layer], dp_metadata, cudagraph_runtime_mode, batch_descriptor, is_padding, skip_compiled, +all_moe_layers/moe_layer_index (torch.compile string workaround). Model code (200+ files) reads only ForwardContext — changing scheduling/graph-mode/parallelism never touches model files.
3.5 allocate_slots → None — control-flow unification
while True:
new_blocks = kv_cache_manager.allocate_slots(request, num_new_tokens)
if new_blocks is not None: break
preempted = self.running.pop() # reclaim budget
self._preempt_request(preempted)
Returning None (not raising) keeps preempt logic in the same while-loop as allocate logic.
4. KV-Cache Management — why 4 layers, not 1
| Layer | File | Owns | Rate of change |
|---|---|---|---|
| KVCacheManager | v1/core/kv_cache_manager.py:114 | request-lifecycle ↔ block allocation (allocate_slots/free/get_computed_blocks) | daily (policy) |
| KVCacheCoordinator | v1/core/kv_cache_coordinator.py:60 | multi-group (MLA+GQA+SWA): logical block id → per-group physical block ids | weekly (model structure) |
| BlockPool | v1/core/block_pool.py:143 | free-list + content-hash cache (get_new_blocks/free_blocks/touch/evict) | monthly (algorithm) |
| KVConnector | distributed/kv_transfer/kv_connector/v1/base.py:171 | cross-worker KV movement (PD disagg / offload / KV Pool) | quarterly (topology) |
4.1 BlockPool — pure metadata, content-hash
v1/core/block_pool.py:162: blocks = [KVCacheBlock(idx) for idx in range(num_gpu_blocks)], free_block_queue, cached_block_hash_to_block = BlockHashToBlockMap(). KVCacheBlock (kv_cache_utils.py:117) is pure metadata — no tensor. The actual bytes live in Worker-side kv_caches: list[torch.Tensor] (model_runner.py:490). This two-layer split lets prefix-cache/swap/sliding-window/compressed-MLA all renumber the same block_ids without touching the pool.
4.2 Coordinator subclasses
UnitaryKVCacheCoordinator (single group), HybridKVCacheCoordinator (MLA+GQA mixed, e.g. DeepSeek-V3), KVCacheCoordinatorNoPrefixCache. MLA's storage_block_size ≠ block_size mapping is encapsulated here — Manager only calls Coordinator's interface.
4.3 Why 4 layers — the MLA forcing function
v0 had a singleBlockSpaceManager. MLA'sstorage_block_size ≠ block_sizeadded a logical→physical mapping that would have scattered across allocate/free/prefix-cache. After the split, this mapping is encapsulated in Coordinator. "Multi-group mapping" changes are isolated to one layer.
Splitting manager↔coordinator: new KV layout (MLA) changes Coordinator only. Splitting coordinator↔blockpool: prefix-cache/eviction changes BlockPool only. Splitting manager↔connector: PD disagg/offload changes Connector only.
5. Parallel & Distributed (TP/PP/DP/EP/CP/EPLB)
| Strategy | Split | Comm | Bubble/cost | vLLM location |
|---|---|---|---|---|
| TP | per-layer matmul (hidden/head) | per-layer all-reduce | 2M/rank | TP_GROUP + ColumnParallelLinear |
| PP | layer segments | P2P [b,h,d] | (p-1)/p | PP_GROUP + IntermediateTensors |
| DP | replica | batch-desc sync | linear scale, CUDA-graph sync needed | dp_utils + cudagraph_sync_dp |
| EP | MoE by expert | all-to-all routing | imbalance=hotspot | eplb/ + distributed/moe/ |
These compose (production runs TP8+EP64+PP4+DP32 simultaneously). Scheduler does NOT know TP/EP — its granularity is "request", not "token on which card". Worker handles via model-internal ColumnParallelLinear/MoEDispatch; ModelRunner never calls comm directly. Ascend HCCL via distributed/pyhccl.py PyHcclCommunicator.
5.1 Context Parallel (PCP/DCP)
| Variant | When | Comm domain | Goal |
|---|---|---|---|
| PCP (Prefill-CP) | prefill phase | independent | split sequence across devices; reduce TTFT |
| DCP (Decode-CP) | decode / chunked-prefill | reuses TP domain (no extra devices) | eliminate TP-domain KV redundancy → free HBM → more concurrency |
cp_size = pcp_size × dcp_size; token-interleave storage via cp_kv_cache_interleave_size (default 1); block_size % cp_kv_cache_interleave_size == 0 required.
5.2 EPLB — Ascend's original MoE contribution
Expert-migration stack: EplbUpdator → policy (FlashLB Hungarian + numba tree-search, FlashTree, SwiftBalance) → D2DExpertWeightLoader → VllmEplbAdaptor. The only net-new subsystem from Ascend to upstream vLLM.
5.3 PD disagg — cutting the Worker boundary, no new module
P node owns Scheduler+Worker(P)+KVConnector(P→D); D node owns Scheduler+Worker(D)+KVConnector(D←P). Scheduler only sees KVCacheBlocks (ids), not which node holds the block — "request lifecycle ⟷ KV physical location" decoupling, the direct payoff of KVCacheManager being independent of Scheduler.
6. Compilation & Graph (CUDA/ACL Graph, Fusion, Quant)
6.1 Three execution modes
| Mode | Dynamic dims | HBM | TTFT | Use |
|---|---|---|---|---|
| FULL | fully fixed (2^k pad) | high | slow (first-token record) | pure decode, max throughput |
| PIECEWISE | dynamic shapes | medium | fast | mixed prefill+decode, long context |
| Eager (NONE) | fully dynamic | low | fastest | debug, encoder-decoder (cross-attn changes every step) |
Dispatch: v1/worker/gpu/cudagraph_utils.py CudagraphDispatcher, candidate key (rounded_num_tokens, num_active_loras); each recorded at warm-up, runtime looks up nearest case.
6.2 ACL Graph — why Ascend cannot just port CUDA Graph
Source: official docs/source/developer_guide/Design_Documents/ACL_Graph.md
Diff A — full replay needs backend-side update_graph_params() hook (NPU-only). Capture phase records per-graph task handle/event/workspace; replay frontend calls update_full_graph_params() → backend runs parameter refresh on update stream via torch.npu.graph_task_update_begin/end(); ExternalEvent enforces ordering. Key fact: an attn backend WITHOUT this hook CANNOT do full-graph replay. Implemented in vllm_ascend/attention/attention_v1.py, mla_v1.py, context_parallel/attention_cp.py, mla_cp.py.
Diff B — ACL capture can still be rejected by runtime resources. Capture sizes are measured, not estimated from CUDA. Tune cudagraph_capture_sizes / max_cudagraph_capture_size.
6.3 Mode normalization (stricter than upstream)
encoder-decoder forced PIECEWISE; use_inductor disabled on graph path; ASCEND_LAUNCH_BLOCKING=1 rejected when graph enabled; Xlite can disable full mode / fall back to FULL_DECODE_ONLY.
6.4 Fusion & Quant
70+ fusion passes in AscendCompiler/GraphFusionPassManager (AllReduce+RMSNorm, QK-Norm+Rope, AllGather-chunk-noop, NoOp, norm-quant). 10+ quant schemes (W4A4/W4A8/W8A8/PDmix/MXFP/FlatQuant). Rule of thumb: MoE→W4A8, dense→W8A8, extreme(EP off)→W4A4. W4A4∧EPLB incompatible = CANN kernel-coverage limit.
7. Ascend Overlay — DSA/SFA/KV Pool/PD/SRF/CPU-binding
7.1 Subsystem × mechanism map
| Subsystem | Mechanism | Key file |
|---|---|---|
| Platform | subclass Platform + config injection | platform.py NPUPlatform (1255L) |
| Attention | 5 backends (ASCEND/MLA/SFA/DS_A/FA3) | attention/*_v1.py |
| Custom ops | CANN custom op + substitution | ops/ 86 files |
| Quant | 10+ QuantConfig subclasses | quantization/ |
| Compilation | ACL Graph + 70 fusion | acl_graph.py, AscendCompiler |
| Comm | HCCL wraps NCCL | pyhccl.py PyHcclCommunicator |
| KV transport | P2P mooncake + ascend_store pool | kv_p2p/mooncake_connector + kv_pool/ascend_store |
| Scheduler | recompute + SRF + profiling-chunk | core/recompute_scheduler.py |
| EPLB | whole subsystem (Ascend original) | eplb/ |
| Patch | 60+ setattr | patch/ |
7.2 Three Ascend original innovations
- Schedule-level (roofline NPU):
RecomputeScheduler (recompute_scheduler.py:98)replaces preemption's offload-to-CPU with drop-and-recompute. Also SRF + profiling-chunk. - EPLB — the net-new vLLB contribution: FlashLB+FlashTree+SwiftBalance, 3-stage updator→policy→D2D migration.
- ACL Graph (§6.2) with update_graph_params hook requirement; GraphFusionPassManager 70 passes.
7.3 KV Pool (Ascend Store) — the memory tier
3 backends: Mooncake (Distributed Store + SSD offload), Memcache (MemFabric), Yuanrong (openEuler etcd+HugeTLB). Plus NVMe SSD offload. Mirror of OS cache→DRAM→swapfile. Tuning: kv_load_failure_policy=recompute (hybrid-attn like DSV4 not yet supported); lease TTL ≥ ASCEND_CONNECT_TIMEOUT+TRANSFORM; fabric mem A3 1GB-aligned; PYTHONHASHSEED consistent across nodes; warm-up 2-3× device-count of 8K-input/1-output requests.
7.4 SRF + CPU-binding + Netloader
SRF: anti head-of-line on P-node; threshold=P70-P85 prompt-len, long_max_wait=long-req P90 under strict-short-first. CPU-binding: default-on ARM since v0.18 (uses npu-smi topo to pick NUMA). Netloader: HCCL send/recv weight transfer, register_model_loader API.
7.5 Patch layer = diagnostic of "missing extension points"
Canonical examples: patch_fused_moe (no FusedMoE-factory-swap iface), patch_cudagraph (CudagraphDispatcher assumes CUDA-graph-only), patch_kv_cache_coordinator (internal state not exposed), patch_mla_prefill_backend (no MLA prefill backend-switch iface). Every heavily-patched subsystem is a candidate for a new upstream extension point.
8. Performance Optimisation Handbook (OS → allocator → HCCL → kernel → fusion)
| Layer | Knob | Effect |
|---|---|---|
| OS | governor→performance; swap=0; auto-NUMA-bal off; sched_migration_cost_ns=50000 | host jitter ms→μs |
| Allocator | jemalloc/TCMalloc | peak-throughput fragmentation reduced |
| torch_npu env | memory/scheduling toggles | CANN best defaults |
| HCCL comm | AIV mode (AI vect core bypasses AI CPU); INTRA_ROCE_ENABLE; RDMA_TC/SL; BUFFSIZE | NPU-only layer: comm ms→sub-ms |
| Fusion | 70+ passes | fewer launches + fewer intermediate HBM writes |
Test practice: unit tests (tests/ut, CPU-runnable) + E2E regression (tests/e2e) + nightly CI (schedule_nightly_test_a2/a3.yaml). Profiling chain: msprobe (NPU kernel timeline) → service_profiling_guide → service_benchmark (recommend 2-3× device-count warm-up of 8K-in/1-out).
Feature-matrix key exclusions
PIECEWISE ⟷ FULL: mutually exclusive. DP+CP: only DCP supports DP. Flashcomm: prefill-only. W4A4∧EPLB/TP: ❌. MLAPO: decode-only. MM input + ACLGraph Full: ❌.
Supported models
20+ mainstream on A2/A3 with full TP+EP+PD-Disagg+Full-Graph: DeepSeek V3→V4, Qwen3, GLM5, Kimi-K2, MiniMax-M2. Gemma4/950 experimental. See docs/source/user_guide/support_matrix/.
9. Generic vLLM Hardware-Adaptation Playbook
9.1 8-step workflow
| # | Goal | Extension point |
|---|---|---|
| 1 | device discovery | Platform.get_device_* + get_attn_backend_cls |
| 2 | paged KV | KVCacheSpec family + Manager + Coordinator + BlockPool (full I1 stack, reuse unchanged) |
| 3 | attention | register_backend (the ONLY must-replace) |
| 4 | other kernels | custom op (ascend uses 4 paths: direct_register / torch_npu / Triton / CANN) |
| 5 | worker/runner | subclass GPUModelRunner (ascend has 3 runners: v1 ACL / v2 piecewise / Xlite) |
| 6 | comm | Communicator wraps NCCL (ascend HCCL via ctypes) |
| 7 | config injection | check_and_update_config + scheduler_cls string swap |
| 8 | tests | tests/ut + tests/e2e |
9.2 MUST / SHOULD / NICE
MUST (won't start): Platform / Attention / paged-KV / GEMM / comm / Worker.
SHOULD (halved without): ACL-graph / quant / Scheduler-tweak.
NICE (Ascend-tier): EPLB / KV-PD / recompute / Xlite.
9.3 Fork vs plugin vs patch
Fork when expansion >25% upstream. Plugin when Platform/Attention/KVConnector/QuantConfig/scheduler_cls have registration points. Patch = workaround; every patch is a break-point each major release.
9.4 7 risk categories
API drift, patch fragility, name leakage, meta omission, config explosion, multi-runner, test gap. Patch layer = the single largest risk surface.
10. Community Pulse (2026 Q2–Q3)
Window: vllm-ascend git log 2026-04-01..07-18, ~1060 commits.
| Topic | Commits | Signal |
|---|---|---|
| kv | 97 | KV full-stack (PD/Pool/CPU-offload) — absolute focus |
| A3 | 64 | new-gen HW ramp |
| quant | 61 | W4A4/W4A8/W8A8/MXFP diversity |
| spec | 53 | spec decode family (Eagle3/DFlash/DSpark/MTP/PCP) |
| 310 | 53 | 310P is a separate HW line (not A3 subset) |
| PCP | 35 | prefill CP |
| eplb | 24 | Ascend original |
| MLA | 15 | DeepSeek compress |
| recompute | 14 | roofline NPU-side |
| Flashcomm | 11 | CANN fused-comm |
| disagg | 8 | PD disagg engineering |
5 advanced principles (only visible in community activity)
- P1 comp/bandwidth ratio decides swap vs recompute (NPU recompute, GPU swap)
- P2 PD disagg projects roofline inter-node (chunked prefill vs PD disagg = two implementations of same insight)
- P3 KV-cache evolves cache → memory tier (HBM → extended pool → SSD offload, mirrors OS)
- P4 host-jitter = last 10% P99 after kernel fusion (CPU binding+msprobe+Netloader converge)
- P5 both ends of Python 95/5 compressed; eventual pressure to move scheduler to C++/Rust
Three-layer enforcement hierarchy
| Level | vLLM | Ascend |
|---|---|---|
| L1 implement-to-exist | paged-KV / single-step scheduler / Platform | HCCL↔NCCL / Platform shim / ACL Graph |
| L2 implement-to-compete | prefix cache / chunked prefill / CPython 95/5 | recompute / KV Pool / SRF / quant diversity |
| L3 implement-to-win | PD disagg / spec decode | CPU binding / profiling-chunk / Netloader / Flashcomm |
11. References & Drill Anchors
11.1 Key source anchors (file:line)
| Anchor | What |
|---|---|
v1/engine/core.py:576 | EngineCore.step (3-line heartbeat) |
v1/core/sched/scheduler.py:418 | schedule() unified loop |
v1/core/kv_cache_manager.py:283 | allocate_slots (returns None on OOM) |
v1/core/block_pool.py:283 | slot_id = block_number*size+offset |
v1/worker/gpu/model_runner.py:1134 | execute_model 5-stage |
v1/worker/gpu/block_table.py:283 | _compute_slot_mappings_kernel |
v1/attention/backend.py | register_backend / Builder / Impl |
forward_context.py:132 | ForwardContext (model↔framework firewall) |
compilation/cuda_graph.py | CUDAGraphRunner |
kv_connector/v1/base.py:171 | KVConnectorBase_V1 |
platforms/interface.py | Platform (6 mandatory methods) |
ascend/platform.py | NPUPlatform |
ascend/attention_v1.py @register_backend | ASCEND / ASCEND_MLA / DSA / SFA / FA3 |
ascend/acl_graph.py | ACLGraphWrapper |
ascend/core/recompute_scheduler.py:98 | RecomputeScheduler |
ascend/eplb/ | EPLB stack (Ascend original) |
ascend/kv_p2p/mooncake_connector.py | MooncakeConnector |
11.2 External links
- v1_guide · v1-alpha-release blog
- arch_overview · attention_backends · cuda_graphs
- Ascend docs · Ascend github
11.3 Raw reports (1:1 traceability)
All original reports preserved in /vllm_research/vllm_analysis_raw/ after this refactor.