Skip to content

Comparisons

Two other libraries put interpretability on vLLM: interp-engine (Neuronpedia / Decode Research) and vLLM-Lens (UK AISI). This page compares nnsight's vLLM integration with each, job by job, and ends with one throughput grid run over all three on the same machine. Every other page in this section avoids comparison; this one is nothing else.


interp-engine

interp-engine is the interpretability engine behind Neuronpedia: a fixed vocabulary of 34 named points (resid_post.10, mlp_act.5, ...) served on a hooked vLLM backend, a CUDA-graph vllm-static backend, and a HuggingFace eager backend, with a validator that checks its captures against TransformerLens and nnsight across 50+ architectures. It is good, careful software, and this section deliberately follows the shape of its documentation page for page so the two can be read side by side.

This page is the comparison the others avoid. It was written against interp-engine 1.3.4 and nnsight 0.8 on the same machine, and the throughput table at the bottom is one grid run over both.

The one-sentence difference

interp-engine answers a closed question well: give me point P, steered by spec S. nnsight runs your Python inside the engine's forward: any module, any computation, any write, on the same request path. Most of what follows is that difference worked through each job.

Addressing

interp-engine nnsight
Unit Address(name, layer[, stream]), 34 canonical names a module on the tree + a side (.input, .output, .inputs, .source.<op>)
Layer index flattened forward order the tree's own indices (layers[10])
Discovery model.points(), the visualizer, SUPPORTED_POINTS.md print(model) on the meta tree, .source for the ops inside a forward
Portability across families the point means the same tensor on every family (attn_out_post vs attn_out on sandwich-norm models is handled for you) you address vLLM's module for this family; the sum of a layer's tuple is the residual on the standard families, and you read the tree to know
Missing on vLLM mlp_pre, mlp_pre_linear, lm_head, attn_gate, expert_weights, expert_indices (fused away); attn_scores/attn_probs by recompute gate_up_proj.output split in two is mlp_pre and mlp_pre_linear; logits_processor(lm_head, h) is the unembed; the pattern is a recompute here too; expert selection is inside the fused kernel for both

The table on Locations is, in effect, interp-engine's point list rewritten as nnsight locations on Qwen3-8B.

Loading

interp-engine nnsight
load_model(id, backend="vllm") — hooked, eager, every point VLLM(id) — every location
backend="vllm-static", static_points=[...] — graphs on, declared taps VLLM(id, taps=[...]) — graphs on, declared taps
backend="vllm-generate" — graphs + compile, no capture plain vllm.LLM, or model.generate(...) outside a block on any engine
backend="eager" (HuggingFace; gradients, batches, attn_probs) TransformersModel(id) — the same block, with gradients
warmup() / shutdown() dispatch=True / process exit
num_gpus=4 tensor_parallel_size=4
configure_static(points) after construction taps are fixed at construction

Both static/tapped engines sit on the same seam — vLLM's breakable CUDA graphs, a callable recorded at capture and run on every replay — and both turn torch.compile off to get it. interp-engine's "auto" static set is resid_post at every layer, read and write; nnsight's taps are whatever module locations you name, read and write.

Capabilities

interp-engine asks first: model.hooks_available, model.points(), model.grad_support, CapabilityUnsupported naming the capability and an alternative. nnsight has less to ask because there is no point table to be absent from — the tree is the capability — and refuses in the same spirit: a non-tap read, a barrier, a chunked prompt, a misspelled sampling keyword and a typo'd tap each raise with the location and the fix (Capabilities and limits).

Reading

Job interp-engine nnsight
one point run_with_cache(model, tokens, [point]) x = loc.clone().save() in a trace
every layer a list of 36 addresses a loop over model.model.layers
while generating capture_generation(...) tracer.all()
a batch eager only many invokes, on vLLM, batched by the scheduler
MoE routing router_logits; selection eager-only mlp.gate.output; selection fused, same
per-head contributions head_contributions(model, cache, 10) z[:, h*d:(h+1)*d] @ W_O[:, h*d:(h+1)*d].T in the block
direct logit attribution client-side, from captured points every layer and head in one forward, in the worker (Attribution)
SAE features capture the point, encode on the client the same, or a feature's live activation in the worker from two rows (SAE features)
gradients eager only HuggingFace path only

Where the two differ in kind: interp-engine's cache is assembled on the client from what the worker shipped; nnsight's block runs on the worker and ships what you saved. Capturing every layer of a 70B model at every step costs interp-engine 3.5× its single-layer rate and nnsight nothing measurable (table below), because the clones never leave the worker until the end.

Attention

Neither library has the attention pattern as a hook on vLLM — the paged kernel never forms it. interp-engine rebuilds it inside capture_attention from captured q/k; on nnsight you rebuild it in the block from self_attn.attn.inputs, in a dozen lines, and both match HuggingFace's eager attention to bf16 noise. interp-engine additionally ships per_head_value, attn_out_gate and split_fused_qkv for the family-specific layouts; on nnsight those are slices you write, and the Attention page gives them for Qwen3. Under tensor parallelism the two part ways: interp-engine's per-head z and direct attribution are single-GPU only (num_gpus > 1 shards the heads across ranks and its off-kernel recompute sees one shard), while nnsight gathers o_proj.input whole before the block reads it, so the per-head material above works at any tensor_parallel_size.

Logit lens

interp-engine's decode_residuals is a method that applies the family's post-unembed arithmetic and runs on the client from captured residuals (a topk variant runs on the worker). nnsight calls the model's own norm and logits_processor in the block, on the worker, so the vocab-wide tensor never travels unless you save it — and it works at every layer in one forward on vLLM, where interp-engine's layer_logits is eager-only.

Writing

interp-engine nnsight
Operations AddSpec, OrthogonalDecompSpec, ProjectionCapSpec, in order per layer any expression
Where resid_post by default; point="z" and the stream points any location — a head's slice of o_proj.input, a router logit, the logits, the sampled id
Positions position_mask, SteerMask.SPECIAL_TOKENS a boolean mask on the rows
During generation the spec is applied every step put the edit under tracer.iter[:N]
Conditional / stateful no yes: if step == 3, a running estimate, a probe's output deciding the write (Conditional interventions)
Activation patching no write of another run's activation a saved tensor written at a position, per layer or per head, batched as invokes (Activation patching)
Ablate a component no write off the residual points mlp.output[:] = 0, a head's slice of o_proj.input, a neuron, a router logit (Ablation)
SAE feature clamp add a fixed decoder direction scale the feature's live activation through generation (SAE features)

The ablate and force rows of the grid — zeroing a head's slice of o_proj.input, and overriding the sampled token — are the two the interp-engine harness could not express on any backend.

Generating

generate_stream yields a GenStep per token with n_logprobs; on vLLM its .logits is None because the sampler never ships the tensor out of the worker. nnsight's model.logits is that tensor, on the worker, readable and writable every step, and logprobs=k rides tracer.result for the portable case. Streaming text is generate_stream on both backends in interp-engine and mode="async" in nnsight; generate_full and tracer.result are the same vLLM RequestOutput.

Chat and tokens

interp-engine's Tokenize helper is the richer one: message_partition, message_spans, GeneratedTurnSpans, compose_assistant_turns, with the DeepSeek-V4 template quirks handled. nnsight hands you the HuggingFace tokenizer and the Chat and tokens page shows offset-mapping spans in a few lines.

Serving

interp-engine nnsight
In-process async every method is async; sync_model facade mode="async"; tracer.backend streams
Concurrency asyncio.gather over capture(...) asyncio.gather over traces
A server your FastAPI app, model built in lifespan nnsight-serve, or your app around VLLM
Clients without a GPU your API VLLM(id) meta tree + trace(..., serve=url)
Instrument every request a spec in a steer() context model.edit() — one block, every request, any tenant

Correctness

interp-engine's validator compares its points against TransformerLens and nnsight/nnterp across 50+ architectures at early, middle and late layers, with the results checked into the repository; that is a level of cross-engine validation nnsight does not have. nnsight's vLLM path is covered by ~195 tests on two GPUs, and every value shown in this section was checked against a HuggingFace forward of the same checkpoint. Its sharded path is checked value by value against a one-rank engine of the same checkpoint — Qwen2.5 at tp=2, DeepSeek-V2-Lite at tp=2 and at tp=4, dcp=2 — and the request accounting under preemption, n > 1, aborted streams and foreign tenants sharing the batch has a test each. Both libraries note the same trap: a value that is the right shape from the wrong place raises nothing.

Throughput

See Throughput, measured at the end of the page — one grid over all three libraries. The interp-engine columns there are vllm (hooked, eager) and vllm-static (CUDA graphs, declared taps); vllm-generate is left out because it is vanilla vLLM under another name (its numbers matched the vanilla column to within 1%), and the eager HuggingFace backends are compared in the text below rather than plotted.

What the grid says about interp-engine:

  • Under graphs the two are the same engine. nnsight taps and IE vllm-static are within noise of each other and of vanilla vLLM on plain generation and single-layer capture, at every size and parallelism — as they should be, sitting on the same vLLM seam.
  • Both eager engines pay the same tax, and it is the driver's: 86 vs 86 tok/s on one GPU, ~70 tok/s flat as GPUs are added.
  • Where the computation happens is the difference that scales. Every-layer capture and the logit lens ship tensors to interp-engine's client per request; nnsight keeps them on the worker. At 70B: 35 vs 10 tok/s for every-layer capture, 35 vs 22 for the lens.
  • Gradients are the one row interp-engine leads, on its eager HuggingFace backend: one forward+backward at 70B takes 531 ms there against 1,216 ms on nnsight's TransformersModel.

vLLM-Lens

vLLM-Lens (UK AISI, MIT) is a vLLM plugin: it registers through vLLM's general_plugins entry point, so an unmodified vllm serve or offline LLM gains activation capture, steering vectors and Garçon-style hooks the moment the package is installed, driven by SamplingParams.extra_args (offline) or vllm_xargs (over the OpenAI API). It also ships an Inspect AI model provider and a set of examples — causal tracing, logit and Jacobian lens, a deception probe, an emotion tracker, an activation oracle. This section was written against vLLM-Lens 1.2.1 on vLLM 0.27.1, the same engine version as the rest of this site.

The Examples group of this section is the vLLM-Lens example set redone in nnsight, so each can be read against its original.

The one-sentence difference

vLLM-Lens exposes one seam — a decoder layer's residual stream, on the way in (pre-hook) or out (post-hook) — and lets you capture it, add to it, or run a pickled function on it. nnsight exposes every module and runs your block interleaved with the forward: the attention projections, the per-head outputs, the router, the logits, the sampled id, and the residual stream are all locations, and the same block reads and writes any of them.

Where a hook can fire

vLLM-Lens nnsight
Residual stream leaving a block output_residual_stream=[l], Hook(layer_indices=[l]) sum(layers[l].output)
Residual stream entering a block Hook(..., pre=True) layers[l].input_layernorm.output[1], embed_tokens.output
q / k / v, per-head z, o_proj input, MLP neurons, the router any of them (Locations)
Pre-sampling logits, the sampled id — (logprobs through the API) model.logits, model.samples, both writable
Inside a module's forward .source ops

Both libraries take the residual stream to be vLLM's (hidden, residual) summed, and both clone before handing it to user code (vLLM-Lens clones for you; on nnsight a kept reference must be cloned — Locations).

Reading

Job vLLM-Lens nnsight
Capture layers extra_args={"output_residual_stream": [15, 20]}out.activations["residual_stream"], (layers, pos, d) .save() on the location; tracer.cache()
Every step of a generation captured per forward pass, stacked tracer.all() / tracer.iter
Compute on the worker Hook(fn); results in ctx.saved, returned as hook_results the block itself
Parameters under TP / PP ctx.get_parameter(name) gathers; prefetch_params for PP activations are gathered; logits_processor(lm_head, h) for the unembed (Tensor parallelism)
A sweep of many prompts register_hooks once, generate per prompt, collect_hook_results model.edit() once, generate(prompts), values on each output
Batch of prompts llm.generate(prompts, params) generate(prompts) (plain) or one invoke per prompt (traced)

Writing

vLLM-Lens nnsight
Additive steering SteeringVector(activations, layer_indices, scale, norm_match, position_indices) layers[l].output[0][:] += scale * v under tracer.iter
Norm-matched norm_match=True: h += scale · ‖h‖ · v/‖v‖ h += scale * h.norm(dim=-1, keepdim=True) * v / v.norm()
Position-specific 3-D activations + position_indices index the rows
Anything else return a tensor from a Hook — at a layer boundary any expression at any location, including the sampler
Persistent register_hooks model.edit()

Serving

This is where vLLM-Lens is strongest. It lives inside vllm serve: the OpenAI-compatible completions and chat endpoints accept vllm_xargs for capture, steering and hooks, the server gains /v1/hooks/* for persistent hooks and parameter prefetch, activations come back base64- encoded in the response, and any OpenAI client — or Inspect, through the bundled provider — can drive it. nnsight's equivalent is nnsight-serve, a single-model server that runs nnsight traces submitted by GPU-less clients and installs engine-wide edits; it does not speak the OpenAI API. An edit installed on an nnsight engine does run on every request the engine serves, whoever sent it, which is the persistent-hook pattern; but the front door for OpenAI-style traffic is vLLM-Lens's.

vLLM-Lens nnsight
Server vllm serve + plugin nnsight-serve
Client protocol OpenAI API + vllm_xargs; VLLMLensClient nnsight traces over HTTP (serve=url)
Code on the server cloudpickled hook functions (arbitrary code; trusted clients only) serialized trace blocks (likewise)
Persistent instrumentation /v1/hooks/register model.edit(serve=url)
Inspect AI provider built in
Streaming the API's mode="async"

Parallelism and engines

vLLM-Lens nnsight
Tensor parallel steering and hooks on every rank; capture on rank 0; a hook that saves Python lists sees them tp_size× every rank runs the block; reads are gathered whole; rank 0 reports
Pipeline parallel yes (prefetch_params for cross-stage weights) no
Expert parallel / MoE yes yes, incl. MoE partial-sum gather
CUDA graphs never — the plugin forces enforce_eager for every engine in the process taps= keeps replay (Performance)
LoRA yes (lora_request; the activation-oracle example) yes (lora_request in the sampling kwargs)
Installed alongside other engines VLLM_LENS_DISABLE=1 to make it a no-op nothing is patched until VLLM(...) is built

The last row matters operationally: vLLM-Lens patches EngineArgs.create_engine_config and LLM.generate at import, so every vLLM engine in a process that has it installed runs eager with the worker extension attached — including one you did not mean to instrument.

The examples

vLLM-Lens example Mechanism there On nnsight
causal_tracing.py pre-hook noise on the subject embeddings, post-hook restore, one HTTP request per (layer, position) Causal tracing — one trace per layer, one invoke per position, batched by the scheduler
logit_lens.py hook with ctx.get_parameter("lm_head.weight"), manual RMSNorm Logit lens — the model's own norm and logits_processor
jacobian_lens.py, jacobian_lens_chat.py hook applying a fitted J_l, prefetched weights; lens fit separately on prime-rl Jacobian lens — Neuronpedia's fitted lens, read out per step; edit() for the chat pattern
deception_probe.py persistent hooks over contrastive prompts, LBFGS probe A linear probeedit() over the prompts; then the probe runs inside the model every step
emotion_tracker.py persistent hooks for direction vectors, per-token projections via chat Concept directions
activation_oracle.py capture, then norm-matched positional steering under a LoRA oracle expressible (norm-matched positional write + lora_request); not reproduced here — it needs the 70B oracle adapter
extract_residual_stream.ipynb per-request and persistent capture, offline and HTTP Capture, Async and servers

Throughput

vLLM-Lens is the fifth series in Throughput, measured; its column ran in its own environment (the plugin forces eager mode on every engine in a process) on the same cards, and the grid gained one row for it: a sweep of 1024 short prompts at one token each, capturing one layer — the activation-extraction workload vLLM-Lens is built for.

  • Plain generation and single-layer capture are a wash. Both libraries hook the same seam and both run the engine eagerly by default: 86–87 tok/s plain, 77–79 capturing one layer. nnsight's taps column is the only one that keeps CUDA graphs, and the only one within a few percent of vanilla.
  • vLLM-Lens pays per layer and per hook. Its hooks are installed on every decoder layer and each does its bookkeeping for every in-flight request on every step, and a steering vector or hook clones the layer's output: capturing all 32 layers halves throughput (37 vs nnsight's 68 tok/s), and a steering vector, a probe or a lens each cost about 40% (48–49 vs 78). nnsight's block visits only the locations it names.
  • The sweep is close. vLLM-Lens's capture rides the request with little per-request setup (1.32 s over 1024 prompts, 1.8× vanilla). An nnsight trace serializes a block per invoke and collects per step (1.6 s, 2.1×); the intended shape for a sweep is model.edit(), which installs the block once — 1.1 s eager, 1.0 s under taps, the fastest capture of the three.
  • Two rows vLLM-Lens cannot express: an ablation inside the attention block and an override of the sampled token, because its hook points are layer boundaries and it has no hook on the sampler.

A trap found while measuring, worth knowing on the nnsight side: writing model.model.layers[16].output inside the block references the model, and each of the 1024 invokes then serializes it — 8.6 s for the sweep. Binding the layer envoy before the trace and using it inside is what the grid shows. Invisible in a single trace, decisive in a sweep (Performance).

  • Tensor parallelism. vLLM-Lens installs its hooks on every rank; at tp=4 plain generation matches the other eager engines (76 tok/s vs 69–72), single-layer capture too (69), but a steering vector or probe costs a quarter (58, 57) where the eager nnsight engine and interp-engine lose nothing, and every-layer capture drops to 37 (nnsight eager 58, taps 208). At 70B/tp=4 the same shape: plain generation and one-layer capture on par or slightly ahead, steering, probe and lens at 18–19 tok/s against the eager engines' 27–29.

Throughput, measured

One harness, all three libraries, the same machine: bf16, A100-80GB, vLLM 0.27.1, transformers 5.15, 512-token prompt, 128 new tokens, greedy, prefix caching off on every engine, 3 processes × 3 timed runs per cell (mean; std ≤ 2% except the HuggingFace-eager rows, which are not plotted). Each dot is a library's throughput on a workload as a share of plain vLLM doing the same generation with nothing attached — vanilla generate for single-stream rows, vanilla 8-concurrent generate for the ×8 rows, and vanilla's own sweep time for the sweep rows (where less time is more). Hollow dots are eager engines, filled dots keep CUDA graphs; hue is the library. The whisker through each dot is the min–max over its repeated runs (nine for most cells: three processes × three timed runs; the 70B and tp=8 panels have six). A cell that is statistically significantly faster than its nnsight counterpart — the eager engines and vLLM-Lens against nnsight eager, vllm-static against nnsight taps — by an exact two-sided Mann-Whitney U test at p < 0.05 and at least 3% apart is bold in the tables; a bold nnsight cell beat every counterpart in its row by the same test. Hover a dot for the number, the run count and the range.

Every panel was measured in one session on one machine, which is what makes the libraries comparable; the absolute tok/s of an eager row is not. A graph engine is bound by the GPU and reproduces: re-run on the same machine while other tenants are running, vanilla vLLM gives 91.8 tok/s against the 92 below and nnsight taps 88.6 against 89. An eager engine spends a Python round trip per module call on the driver, so its throughput follows whatever CPU the host has left, and the whole nnsight eager column on that re-run came back between 44 and 58 tok/s instead of 68 to 86. Plain vLLM with enforce_eager=True and nothing attached moves with it (52.1 tok/s there, against nnsight's 54.1), which is the reason the eager rows on Performance are published as a share of that engine rather than as a rate.

Other models and scenarios

The same grid on more models and two more workload shapes (three processes each; the DeepSeek panel has no interp-engine column, whose runner was not pointed at it):

  • Qwen3-8B (36 layers with QK-norm, the model this section's examples use). The eager engines are all at ~74% of vanilla on plain generation (67–69 tok/s vs 91) where on Llama-8B they were at 93%: the eager tax scales with the number of module calls per layer, and Qwen3 has more of them. The graph engines are unaffected (nnsight taps 88, interp-engine static 88). Everything else has the Llama shape — nnsight leads on every-layer capture and the lens, interp-engine's hooked engine leads by ~10% on single-layer capture, steering and the probe, vLLM-Lens pays ~35% for a hook or steering vector.
  • Qwen1.5-MoE-A2.7B (60 experts, 4 active). The eager engines collapse to 20% of vanilla (40 tok/s vs 210): an MoE layer is many small kernels, and each is a Python round trip on an eager path. nnsight's taps keep 98% (205); interp-engine's static engine 86% (181); vLLM-Lens is an eager engine and sits with the eager nnsight and interp-engine at 40–42. At 8 concurrent requests nnsight taps (1,091) and interp-engine static (874) both exceed vanilla (671), which turns out to be vLLM's torch.compile path losing on this model — see the note under the MoE chart.
  • DeepSeek-V2-Lite (MLA attention + MoE, 27 layers, 16B). Same picture, stronger: eager engines at 19% of vanilla (31 vs 164), nnsight taps at 98% (161). The MLA path (kv_a_proj, kv_b_proj, the absorbed decode kernel) adds module calls that only a graph engine hides.
  • Llama-3.2-1B. With a small model the fixed per-step Python cost dominates: the eager engines are at 37–39% of vanilla, the graph engines at 94–95%, and vLLM-Lens's hook costs show most clearly (steering 116 vs interp-engine 145, every-layer capture 96 vs nnsight 119).
  • Long context (2048-token prompt, 512 new tokens, Llama-8B). Longer decode amortizes per-step overheads: every eager engine moves up a few points relative to the 512/128 grid, the ordering is unchanged, and vLLM-Lens's per-hook cost persists (steering 50, probe 49 vs the eager engines' 75–84).
  • 32 concurrent requests (Llama-8B). Plain generation is a wash for every engine (1,430–1,470 tok/s vs vanilla 1,523). Capturing one layer on all 32 streams costs nnsight 6–10% (1,330 eager, 1,367 taps), interp-engine 4–8% (1,408 static, 1,371 hooked) and vLLM-Lens 25% (1,100): at this batch size the per-request bookkeeping that all three do on every step is what shows, and the graph engines' decode advantage is mostly spent.
Llama-3.1-8B, one GPU Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 86 (93% of vanilla; 9 runs, 84–87) interp-engine vllm-static: 90 (97% of vanilla; 9 runs, 90–90) vLLM-Lens: 86 (94% of vanilla; 9 runs, 84–87) nnsight eager: 86 (93% of vanilla; 9 runs, 84–87) nnsight taps (CUDA graphs): 89 (97% of vanilla; 9 runs, 89–89) generate, 8 concurrent interp-engine vllm: 580 (94% of vanilla; 9 runs, 579–582) interp-engine vllm-static: 596 (97% of vanilla; 9 runs, 594–597) vLLM-Lens: 578 (94% of vanilla; 9 runs, 577–580) nnsight eager: 577 (93% of vanilla; 9 runs, 576–578) nnsight taps (CUDA graphs): 597 (97% of vanilla; 9 runs, 597–598) capture 1 layer, every step interp-engine vllm: 84 (91% of vanilla; 9 runs, 81–87); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 89 (97% of vanilla; 9 runs, 89–89) vLLM-Lens: 75 (81% of vanilla; 9 runs, 72–77); significantly slower than the counterpart (p<0.05) nnsight eager: 79 (85% of vanilla; 9 runs, 78–80) nnsight taps (CUDA graphs): 89 (96% of vanilla; 9 runs, 88–89) capture every layer, every step interp-engine vllm: 48 (52% of vanilla; 9 runs, 46–51); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 71 (77% of vanilla; 9 runs, 68–74); significantly slower than the counterpart (p<0.05) vLLM-Lens: 37 (40% of vanilla; 9 runs, 36–38); significantly slower than the counterpart (p<0.05) nnsight eager: 68 (73% of vanilla; 9 runs, 67–69); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 88 (96% of vanilla; 9 runs, 88–88); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 568 (92% of vanilla; 9 runs, 561–572); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 588 (95% of vanilla; 9 runs, 585–590) vLLM-Lens: 470 (76% of vanilla; 9 runs, 459–478); significantly slower than the counterpart (p<0.05) nnsight eager: 529 (86% of vanilla; 9 runs, 524–536) nnsight taps (CUDA graphs): 577 (93% of vanilla; 9 runs, 576–578) additive steering, 1 layer interp-engine vllm: 85 (93% of vanilla; 9 runs, 83–87); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 89 (97% of vanilla; 9 runs, 89–89) vLLM-Lens: 49 (53% of vanilla; 9 runs, 48–50); significantly slower than the counterpart (p<0.05) nnsight eager: 78 (85% of vanilla; 9 runs, 76–80) nnsight taps (CUDA graphs): 89 (96% of vanilla; 9 runs, 89–89) logit lens every step interp-engine vllm: 63 (68% of vanilla; 9 runs, 62–64); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 65 (71% of vanilla; 9 runs, 65–66); significantly slower than the counterpart (p<0.05) vLLM-Lens: 47 (51% of vanilla; 9 runs, 47–48); significantly slower than the counterpart (p<0.05) nnsight eager: 78 (84% of vanilla; 9 runs, 76–79); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 84 (91% of vanilla; 9 runs, 84–84); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 84 (92% of vanilla; 9 runs, 83–86); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 89 (96% of vanilla; 9 runs, 89–89) vLLM-Lens: 49 (53% of vanilla; 9 runs, 48–50); significantly slower than the counterpart (p<0.05) nnsight eager: 78 (84% of vanilla; 9 runs, 77–79) nnsight taps (CUDA graphs): 89 (96% of vanilla; 9 runs, 88–89) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 79 (85% of vanilla; 9 runs, 78–79) nnsight taps (CUDA graphs): 89 (96% of vanilla; 9 runs, 89–89) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 79 (86% of vanilla; 9 runs, 78–81) nnsight taps (CUDA graphs): 89 (96% of vanilla; 9 runs, 88–89) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 1.33 s (56% of vanilla; 9 runs, 1.30 s–1.36 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.60 s (47% of vanilla; 9 runs, 1.55 s–1.63 s) nnsight taps (CUDA graphs): 1.48 s (50% of vanilla; 9 runs, 1.46 s–1.50 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 1.09 s (69% of vanilla; 9 runs, 1.05 s–1.11 s) nnsight taps (CUDA graphs): 1.00 s (74% of vanilla; 9 runs, 0.98 s–1.04 s)
The numbers — Llama-3.1-8B, one GPU (tok/s; sweeps in seconds)
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 92 86 89 86 90 86
generate, 8 concurrent 618 577 597 580 596 578
capture 1 layer, every step · 79 89 84 89 75
capture every layer, every step · 68 88 48 71 37
capture 1 layer, 8 concurrent · 529 577 568 588 470
additive steering, 1 layer · 78 89 85 89 49
logit lens every step · 78 84 63 65 47
linear probe every step · 78 89 84 89 49
zero one attention head every step · 79 89
override the sampled token every step · 79 89
sweep: 1024 × 1 token, capture 1 layer, per request 0.75 s 1.60 s 1.48 s · · 1.33 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 1.09 s 1.00 s · ·
Llama-3.1-8B, tensor-parallel 2 Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 71 (48% of vanilla; 9 runs, 70–72) interp-engine vllm-static: 142 (96% of vanilla; 9 runs, 142–142) vLLM-Lens: 75 (51% of vanilla; 9 runs, 74–76); significantly faster than the counterpart (p<0.05) nnsight eager: 72 (48% of vanilla; 9 runs, 70–73) nnsight taps (CUDA graphs): 142 (96% of vanilla; 9 runs, 141–142) generate, 8 concurrent interp-engine vllm: 520 (56% of vanilla; 9 runs, 514–523) interp-engine vllm-static: 893 (96% of vanilla; 9 runs, 888–898) vLLM-Lens: 537 (57% of vanilla; 9 runs, 523–551) nnsight eager: 525 (56% of vanilla; 9 runs, 517–534) nnsight taps (CUDA graphs): 898 (96% of vanilla; 9 runs, 896–900) capture 1 layer, every step interp-engine vllm: 71 (48% of vanilla; 9 runs, 69–72); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 139 (94% of vanilla; 9 runs, 139–140) vLLM-Lens: 67 (45% of vanilla; 9 runs, 66–68) nnsight eager: 67 (45% of vanilla; 9 runs, 66–68) nnsight taps (CUDA graphs): 140 (95% of vanilla; 9 runs, 140–140) capture every layer, every step interp-engine vllm: 36 (24% of vanilla; 9 runs, 34–37); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 65 (44% of vanilla; 9 runs, 60–69); significantly slower than the counterpart (p<0.05) vLLM-Lens: 35 (23% of vanilla; 9 runs, 34–36); significantly slower than the counterpart (p<0.05) nnsight eager: 59 (40% of vanilla; 9 runs, 58–59); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 138 (94% of vanilla; 9 runs, 138–138); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 507 (54% of vanilla; 9 runs, 497–513); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 859 (92% of vanilla; 9 runs, 849–866) vLLM-Lens: 424 (45% of vanilla; 9 runs, 414–438); significantly slower than the counterpart (p<0.05) nnsight eager: 462 (49% of vanilla; 9 runs, 457–466) nnsight taps (CUDA graphs): 851 (91% of vanilla; 9 runs, 843–858) additive steering, 1 layer interp-engine vllm: 72 (49% of vanilla; 9 runs, 71–73); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 139 (94% of vanilla; 9 runs, 138–140) vLLM-Lens: 53 (36% of vanilla; 9 runs, 52–54); significantly slower than the counterpart (p<0.05) nnsight eager: 66 (44% of vanilla; 9 runs, 64–67) nnsight taps (CUDA graphs): 139 (94% of vanilla; 9 runs, 139–139) logit lens every step interp-engine vllm: 43 (29% of vanilla; 9 runs, 42–44); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 59 (40% of vanilla; 9 runs, 56–61); significantly slower than the counterpart (p<0.05) vLLM-Lens: 51 (34% of vanilla; 9 runs, 50–52); significantly slower than the counterpart (p<0.05) nnsight eager: 66 (45% of vanilla; 9 runs, 65–67); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 134 (91% of vanilla; 9 runs, 134–134); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 72 (49% of vanilla; 9 runs, 71–73); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 139 (94% of vanilla; 9 runs, 137–139) vLLM-Lens: 53 (36% of vanilla; 9 runs, 52–53); significantly slower than the counterpart (p<0.05) nnsight eager: 66 (45% of vanilla; 9 runs, 64–66) nnsight taps (CUDA graphs): 138 (94% of vanilla; 9 runs, 138–139) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 66 (45% of vanilla; 9 runs, 65–67) nnsight taps (CUDA graphs): o_proj is not tapped on this engine (taps carry layer outputs only) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 67 (45% of vanilla; 9 runs, 66–68) nnsight taps (CUDA graphs): 140 (95% of vanilla; 9 runs, 139–140) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 1.17 s (42% of vanilla; 9 runs, 1.13 s–1.20 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.33 s (37% of vanilla; 3 runs, 1.31 s–1.34 s) nnsight taps (CUDA graphs): 1.42 s (35% of vanilla; 3 runs, 1.34 s–1.50 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 1.05 s (47% of vanilla; 3 runs, 1.00 s–1.15 s) nnsight taps (CUDA graphs): 1.05 s (47% of vanilla; 3 runs, 1.01 s–1.10 s)
The numbers — Llama-3.1-8B, tp=2
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 148 72 142 71 142 75
generate, 8 concurrent 933 525 898 520 893 537
capture 1 layer, every step · 67 140 71 139 67
capture every layer, every step · 59 138 36 65 35
capture 1 layer, 8 concurrent · 462 851 507 859 424
additive steering, 1 layer · 66 139 72 139 53
logit lens every step · 66 134 43 59 51
linear probe every step · 66 138 72 139 53
zero one attention head every step · 66
override the sampled token every step · 67 140
sweep: 1024 × 1 token, capture 1 layer, per request 0.49 s 1.33 s 1.42 s · · 1.17 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 1.05 s 1.05 s · ·
Llama-3.1-8B, tensor-parallel 4 Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 69 (30% of vanilla; 9 runs, 68–70); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 216 (95% of vanilla; 9 runs, 214–216) vLLM-Lens: 76 (33% of vanilla; 9 runs, 74–77); significantly faster than the counterpart (p<0.05) nnsight eager: 72 (31% of vanilla; 9 runs, 70–73) nnsight taps (CUDA graphs): 216 (94% of vanilla; 9 runs, 215–217) generate, 8 concurrent interp-engine vllm: 512 (36% of vanilla; 9 runs, 505–519); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 1,345 (94% of vanilla; 9 runs, 1,331–1,357) vLLM-Lens: 550 (38% of vanilla; 9 runs, 538–558); significantly faster than the counterpart (p<0.05) nnsight eager: 532 (37% of vanilla; 9 runs, 520–541) nnsight taps (CUDA graphs): 1,355 (95% of vanilla; 9 runs, 1,343–1,367) capture 1 layer, every step interp-engine vllm: 69 (30% of vanilla; 9 runs, 66–71); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 206 (90% of vanilla; 9 runs, 205–206); significantly slower than the counterpart (p<0.05) vLLM-Lens: 69 (30% of vanilla; 9 runs, 68–70); significantly faster than the counterpart (p<0.05) nnsight eager: 66 (29% of vanilla; 9 runs, 64–68) nnsight taps (CUDA graphs): 213 (93% of vanilla; 9 runs, 212–213); significantly faster than every counterpart (p<0.05) capture every layer, every step interp-engine vllm: 29 (13% of vanilla; 9 runs, 28–31); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 54 (24% of vanilla; 9 runs, 49–58); significantly slower than the counterpart (p<0.05) vLLM-Lens: 37 (16% of vanilla; 9 runs, 36–38); significantly slower than the counterpart (p<0.05) nnsight eager: 58 (26% of vanilla; 9 runs, 58–59); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 208 (91% of vanilla; 9 runs, 207–208); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 482 (34% of vanilla; 9 runs, 470–496); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 1,163 (81% of vanilla; 9 runs, 1,125–1,201); significantly slower than the counterpart (p<0.05) vLLM-Lens: 430 (30% of vanilla; 9 runs, 420–439); significantly slower than the counterpart (p<0.05) nnsight eager: 463 (32% of vanilla; 9 runs, 454–476) nnsight taps (CUDA graphs): 1,256 (88% of vanilla; 9 runs, 1,243–1,265); significantly faster than every counterpart (p<0.05) additive steering, 1 layer interp-engine vllm: 71 (31% of vanilla; 9 runs, 69–72); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 210 (92% of vanilla; 9 runs, 207–211) vLLM-Lens: 58 (25% of vanilla; 9 runs, 56–59); significantly slower than the counterpart (p<0.05) nnsight eager: 66 (29% of vanilla; 9 runs, 65–67) nnsight taps (CUDA graphs): 210 (92% of vanilla; 9 runs, 209–211) logit lens every step interp-engine vllm: 33 (15% of vanilla; 9 runs, 33–34); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 48 (21% of vanilla; 9 runs, 47–49); significantly slower than the counterpart (p<0.05) vLLM-Lens: 55 (24% of vanilla; 9 runs, 54–56); significantly slower than the counterpart (p<0.05) nnsight eager: 66 (29% of vanilla; 9 runs, 65–67); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 205 (90% of vanilla; 9 runs, 204–205); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 69 (30% of vanilla; 9 runs, 67–71); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 206 (90% of vanilla; 9 runs, 204–208) vLLM-Lens: 57 (25% of vanilla; 9 runs, 56–58); significantly slower than the counterpart (p<0.05) nnsight eager: 66 (29% of vanilla; 9 runs, 66–67) nnsight taps (CUDA graphs): 209 (91% of vanilla; 9 runs, 208–209) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 66 (29% of vanilla; 9 runs, 63–67) nnsight taps (CUDA graphs): o_proj is not tapped on this engine (taps carry layer outputs only) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 67 (29% of vanilla; 9 runs, 66–68) nnsight taps (CUDA graphs): 213 (93% of vanilla; 9 runs, 212–213) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 1.00 s (33% of vanilla; 9 runs, 0.96 s–1.06 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.20 s (28% of vanilla; 3 runs, 1.18 s–1.21 s) nnsight taps (CUDA graphs): 1.18 s (28% of vanilla; 3 runs, 1.16 s–1.21 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 1.46 s (23% of vanilla; 3 runs, 1.30 s–1.69 s) nnsight taps (CUDA graphs): 1.35 s (25% of vanilla; 3 runs, 1.21 s–1.54 s)
The numbers — Llama-3.1-8B, tp=4
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 228 72 216 69 216 76
generate, 8 concurrent 1,431 532 1,355 512 1,345 550
capture 1 layer, every step · 66 213 69 206 69
capture every layer, every step · 58 208 29 54 37
capture 1 layer, 8 concurrent · 463 1,256 482 1,163 430
additive steering, 1 layer · 66 210 71 210 58
logit lens every step · 66 205 33 48 55
linear probe every step · 66 209 69 206 57
zero one attention head every step · 66
override the sampled token every step · 67 213
sweep: 1024 × 1 token, capture 1 layer, per request 0.33 s 1.20 s 1.18 s · · 1.00 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 1.46 s 1.35 s · ·
Llama-3.1-70B, tensor-parallel 4 Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 29 (79% of vanilla; 9 runs, 29–29) interp-engine vllm-static: 36 (98% of vanilla; 9 runs, 36–36) vLLM-Lens: 32 (87% of vanilla; 9 runs, 32–32); significantly faster than the counterpart (p<0.05) nnsight eager: 30 (81% of vanilla; 9 runs, 29–30) nnsight taps (CUDA graphs): 36 (97% of vanilla; 9 runs, 36–36) generate, 8 concurrent interp-engine vllm: 203 (86% of vanilla; 9 runs, 199–207) interp-engine vllm-static: 226 (96% of vanilla; 9 runs, 224–228) vLLM-Lens: 214 (91% of vanilla; 9 runs, 212–216); significantly faster than the counterpart (p<0.05) nnsight eager: 200 (85% of vanilla; 9 runs, 197–203) nnsight taps (CUDA graphs): 226 (96% of vanilla; 9 runs, 226–227) capture 1 layer, every step interp-engine vllm: 29 (79% of vanilla; 9 runs, 29–30); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 35 (95% of vanilla; 9 runs, 35–35) vLLM-Lens: 28 (77% of vanilla; 9 runs, 27–29); significantly faster than the counterpart (p<0.05) nnsight eager: 27 (75% of vanilla; 9 runs, 27–28) nnsight taps (CUDA graphs): 35 (97% of vanilla; 9 runs, 35–36) capture every layer, every step interp-engine vllm: 7 (20% of vanilla; 9 runs, 7–8); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 10 (27% of vanilla; 9 runs, 9–11); significantly slower than the counterpart (p<0.05) vLLM-Lens: 10 (26% of vanilla; 9 runs, 9–10); significantly slower than the counterpart (p<0.05) nnsight eager: 23 (63% of vanilla; 9 runs, 23–24); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 35 (96% of vanilla; 9 runs, 35–35); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 187 (80% of vanilla; 9 runs, 184–190); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 212 (90% of vanilla; 9 runs, 209–214); significantly slower than the counterpart (p<0.05) vLLM-Lens: 170 (72% of vanilla; 9 runs, 165–175); significantly slower than the counterpart (p<0.05) nnsight eager: 179 (76% of vanilla; 9 runs, 178–181) nnsight taps (CUDA graphs): 220 (94% of vanilla; 9 runs, 219–220); significantly faster than every counterpart (p<0.05) additive steering, 1 layer interp-engine vllm: 29 (79% of vanilla; 9 runs, 28–29); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 35 (96% of vanilla; 9 runs, 35–35) vLLM-Lens: 19 (51% of vanilla; 9 runs, 19–19); significantly slower than the counterpart (p<0.05) nnsight eager: 27 (74% of vanilla; 9 runs, 27–27) nnsight taps (CUDA graphs): 35 (97% of vanilla; 9 runs, 35–35) logit lens every step interp-engine vllm: 19 (53% of vanilla; 9 runs, 18–20); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 22 (61% of vanilla; 9 runs, 22–23); significantly slower than the counterpart (p<0.05) vLLM-Lens: 18 (49% of vanilla; 9 runs, 18–18); significantly slower than the counterpart (p<0.05) nnsight eager: 27 (75% of vanilla; 9 runs, 27–28); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 35 (96% of vanilla; 9 runs, 35–35); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 29 (79% of vanilla; 9 runs, 29–29); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 35 (96% of vanilla; 9 runs, 35–35) vLLM-Lens: 19 (51% of vanilla; 9 runs, 18–19); significantly slower than the counterpart (p<0.05) nnsight eager: 27 (74% of vanilla; 9 runs, 26–27) nnsight taps (CUDA graphs): 35 (97% of vanilla; 9 runs, 35–35) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 27 (74% of vanilla; 9 runs, 26–28) nnsight taps (CUDA graphs): 35 (97% of vanilla; 9 runs, 35–35) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 27 (75% of vanilla; 9 runs, 27–28) nnsight taps (CUDA graphs): 35 (97% of vanilla; 9 runs, 35–36)
The numbers — Llama-3.1-70B, tp=4
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 37 30 36 29 36 32
generate, 8 concurrent 235 200 226 203 226 214
capture 1 layer, every step · 27 35 29 35 28
capture every layer, every step · 23 35 7 10 10
capture 1 layer, 8 concurrent · 179 220 187 212 170
additive steering, 1 layer · 27 35 29 35 19
logit lens every step · 27 35 19 22 18
linear probe every step · 27 35 29 35 19
zero one attention head every step · 27 35
override the sampled token every step · 27 35
Qwen3-8B, one GPU Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 69 (76% of vanilla; 9 runs, 68–70) interp-engine vllm-static: 88 (96% of vanilla; 9 runs, 88–88) vLLM-Lens: 68 (75% of vanilla; 9 runs, 67–70) nnsight eager: 67 (74% of vanilla; 9 runs, 66–69) nnsight taps (CUDA graphs): 88 (96% of vanilla; 9 runs, 87–88) generate, 8 concurrent interp-engine vllm: 496 (82% of vanilla; 9 runs, 493–501); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 580 (96% of vanilla; 9 runs, 580–580) vLLM-Lens: 490 (81% of vanilla; 9 runs, 482–496); significantly faster than the counterpart (p<0.05) nnsight eager: 471 (78% of vanilla; 9 runs, 464–477) nnsight taps (CUDA graphs): 583 (96% of vanilla; 9 runs, 581–584) capture 1 layer, every step interp-engine vllm: 67 (73% of vanilla; 9 runs, 65–68); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 87 (96% of vanilla; 9 runs, 87–87) vLLM-Lens: 60 (66% of vanilla; 9 runs, 58–61) nnsight eager: 60 (66% of vanilla; 9 runs, 60–61) nnsight taps (CUDA graphs): 87 (96% of vanilla; 9 runs, 87–87) capture every layer, every step interp-engine vllm: 39 (43% of vanilla; 9 runs, 37–40); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 67 (73% of vanilla; 9 runs, 64–70); significantly slower than the counterpart (p<0.05) vLLM-Lens: 32 (35% of vanilla; 9 runs, 31–32); significantly slower than the counterpart (p<0.05) nnsight eager: 52 (58% of vanilla; 6 runs, 52–53); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 86 (95% of vanilla; 9 runs, 86–87); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 475 (79% of vanilla; 9 runs, 460–483); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 568 (94% of vanilla; 9 runs, 563–572) vLLM-Lens: 387 (64% of vanilla; 9 runs, 358–396); significantly slower than the counterpart (p<0.05) nnsight eager: 419 (69% of vanilla; 6 runs, 414–422) nnsight taps (CUDA graphs): 562 (93% of vanilla; 9 runs, 561–563) additive steering, 1 layer interp-engine vllm: 67 (74% of vanilla; 9 runs, 66–70); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 87 (96% of vanilla; 9 runs, 87–87) vLLM-Lens: 42 (47% of vanilla; 9 runs, 42–43); significantly slower than the counterpart (p<0.05) nnsight eager: 59 (65% of vanilla; 6 runs, 59–60) nnsight taps (CUDA graphs): 87 (95% of vanilla; 9 runs, 86–87) logit lens every step interp-engine vllm: 51 (57% of vanilla; 9 runs, 50–53); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 63 (69% of vanilla; 9 runs, 62–63); significantly slower than the counterpart (p<0.05) vLLM-Lens: 41 (45% of vanilla; 9 runs, 40–41); significantly slower than the counterpart (p<0.05) nnsight eager: 61 (67% of vanilla; 6 runs, 60–61); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 82 (90% of vanilla; 9 runs, 82–82); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 68 (75% of vanilla; 9 runs, 66–70); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 87 (95% of vanilla; 9 runs, 84–87) vLLM-Lens: 42 (46% of vanilla; 9 runs, 41–42); significantly slower than the counterpart (p<0.05) nnsight eager: 60 (66% of vanilla; 6 runs, 60–60) nnsight taps (CUDA graphs): 87 (95% of vanilla; 9 runs, 86–87) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 61 (67% of vanilla; 6 runs, 60–62) nnsight taps (CUDA graphs): 87 (96% of vanilla; 9 runs, 87–87) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 60 (66% of vanilla; 6 runs, 58–61) nnsight taps (CUDA graphs): 87 (96% of vanilla; 9 runs, 87–87) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 1.41 s (56% of vanilla; 9 runs, 1.36 s–1.44 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.74 s (45% of vanilla; 6 runs, 1.70 s–1.81 s) nnsight taps (CUDA graphs): 1.59 s (49% of vanilla; 9 runs, 1.54 s–1.63 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 1.19 s (66% of vanilla; 6 runs, 1.17 s–1.20 s) nnsight taps (CUDA graphs): 1.04 s (75% of vanilla; 9 runs, 1.00 s–1.08 s)
The numbers — Qwen3-8B, one GPU
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 91 67 88 69 88 68
generate, 8 concurrent 604 471 583 496 580 490
capture 1 layer, every step · 60 87 67 87 60
capture every layer, every step · 52 86 39 67 32
capture 1 layer, 8 concurrent · 419 562 475 568 387
additive steering, 1 layer · 59 87 67 87 42
logit lens every step · 61 82 51 63 41
linear probe every step · 60 87 68 87 42
zero one attention head every step · 61 87
override the sampled token every step · 60 87
sweep: 1024 × 1 token, capture 1 layer, per request 0.78 s 1.74 s 1.59 s · · 1.41 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 1.19 s 1.04 s · ·
Qwen1.5-MoE-A2.7B (mixture of experts), one GPU Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% 125% 150% vanilla vLLM generate interp-engine vllm: 41 (20% of vanilla; 9 runs, 40–42) interp-engine vllm-static: 181 (86% of vanilla; 9 runs, 179–182); significantly slower than the counterpart (p<0.05) vLLM-Lens: 42 (20% of vanilla; 9 runs, 41–42); significantly faster than the counterpart (p<0.05) nnsight eager: 40 (19% of vanilla; 9 runs, 40–42) nnsight taps (CUDA graphs): 205 (98% of vanilla; 9 runs, 204–205); significantly faster than every counterpart (p<0.05) generate, 8 concurrent interp-engine vllm: 309 (46% of vanilla; 9 runs, 298–317) interp-engine vllm-static: 874 (130% of vanilla; 9 runs, 867–876); significantly slower than the counterpart (p<0.05) vLLM-Lens: 312 (46% of vanilla; 9 runs, 309–314); significantly faster than the counterpart (p<0.05) nnsight eager: 303 (45% of vanilla; 9 runs, 295–311) nnsight taps (CUDA graphs): 1,091 (163% of vanilla; 9 runs, 1,090–1,093); significantly faster than every counterpart (p<0.05) capture 1 layer, every step interp-engine vllm: 41 (19% of vanilla; 9 runs, 40–41); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 179 (86% of vanilla; 9 runs, 178–180); significantly slower than the counterpart (p<0.05) vLLM-Lens: 39 (19% of vanilla; 9 runs, 37–40) nnsight eager: 38 (18% of vanilla; 9 runs, 37–39) nnsight taps (CUDA graphs): 202 (97% of vanilla; 9 runs, 202–203); significantly faster than every counterpart (p<0.05) capture every layer, every step interp-engine vllm: 34 (16% of vanilla; 9 runs, 33–34); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 148 (70% of vanilla; 9 runs, 143–153); significantly slower than the counterpart (p<0.05) vLLM-Lens: 33 (16% of vanilla; 9 runs, 32–33); significantly slower than the counterpart (p<0.05) nnsight eager: 35 (17% of vanilla; 9 runs, 34–36); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 200 (95% of vanilla; 9 runs, 200–200); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 304 (45% of vanilla; 9 runs, 299–311); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 863 (129% of vanilla; 9 runs, 856–867); significantly slower than the counterpart (p<0.05) vLLM-Lens: 279 (42% of vanilla; 9 runs, 275–285) nnsight eager: 280 (42% of vanilla; 9 runs, 269–287) nnsight taps (CUDA graphs): 1,023 (152% of vanilla; 9 runs, 1,019–1,026); significantly faster than every counterpart (p<0.05) additive steering, 1 layer interp-engine vllm: 41 (20% of vanilla; 9 runs, 41–42); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 180 (86% of vanilla; 9 runs, 179–180); significantly slower than the counterpart (p<0.05) vLLM-Lens: 36 (17% of vanilla; 9 runs, 35–37); significantly slower than the counterpart (p<0.05) nnsight eager: 38 (18% of vanilla; 9 runs, 37–39) nnsight taps (CUDA graphs): 201 (96% of vanilla; 9 runs, 200–201); significantly faster than every counterpart (p<0.05) logit lens every step interp-engine vllm: 34 (16% of vanilla; 9 runs, 33–35); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 99 (47% of vanilla; 9 runs, 96–102); significantly slower than the counterpart (p<0.05) vLLM-Lens: 35 (17% of vanilla; 9 runs, 34–36); significantly slower than the counterpart (p<0.05) nnsight eager: 38 (18% of vanilla; 9 runs, 37–38); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 188 (90% of vanilla; 9 runs, 187–188); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 40 (19% of vanilla; 9 runs, 39–41); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 179 (85% of vanilla; 9 runs, 178–180); significantly slower than the counterpart (p<0.05) vLLM-Lens: 36 (17% of vanilla; 9 runs, 35–36); significantly slower than the counterpart (p<0.05) nnsight eager: 38 (18% of vanilla; 9 runs, 38–39) nnsight taps (CUDA graphs): 200 (95% of vanilla; 9 runs, 199–200); significantly faster than every counterpart (p<0.05) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 38 (18% of vanilla; 9 runs, 36–39) nnsight taps (CUDA graphs): 202 (97% of vanilla; 9 runs, 202–203) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 38 (18% of vanilla; 9 runs, 37–39) nnsight taps (CUDA graphs): 202 (96% of vanilla; 9 runs, 201–202) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 0.82 s (54% of vanilla; 9 runs, 0.80 s–0.91 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.36 s (32% of vanilla; 6 runs, 1.33 s–1.38 s) nnsight taps (CUDA graphs): 1.27 s (35% of vanilla; 9 runs, 1.25 s–1.30 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 0.71 s (62% of vanilla; 9 runs, 0.69 s–0.74 s) nnsight taps (CUDA graphs): 0.66 s (67% of vanilla; 9 runs, 0.64 s–0.67 s)

The vanilla column on this model is vLLM's default engine: CUDA graphs and torch.compile. On this MoE the compiled path is the slow one at 8 concurrent requests. Measured directly on plain vLLM, same prompts and settings: eager 315 tok/s; torch.compile alone 245 (the compiled forward is slower than eager on this model); compile + CUDA graphs, the default, 669; CUDA graphs with compilation off 1,095 (1,099 in the breakable-graph mode nnsight's taps use). Enabling vLLM's own custom kernels under compile (custom_ops=all) changes nothing (668), so it is the inductor-compiled forward itself, not a missing fused op. nnsight taps (1,091) and interp-engine static (874) are therefore not beating vLLM; they run the un-compiled graph path that vLLM's default loses to on this model. Single-request generation is unaffected (210 default vs 205 taps).

The numbers — Qwen1.5-MoE-A2.7B, one GPU
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 210 40 205 41 181 42
generate, 8 concurrent 671 303 1,091 309 874 312
capture 1 layer, every step · 38 202 41 179 39
capture every layer, every step · 35 200 34 148 33
capture 1 layer, 8 concurrent · 280 1,023 304 863 279
additive steering, 1 layer · 38 201 41 180 36
logit lens every step · 38 188 34 99 35
linear probe every step · 38 200 40 179 36
zero one attention head every step · 38 202
override the sampled token every step · 38 202
sweep: 1024 × 1 token, capture 1 layer, per request 0.44 s 1.36 s 1.27 s · · 0.82 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 0.71 s 0.66 s · ·
Llama-3.2-1B, one GPU Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 150 (38% of vanilla; 9 runs, 147–154); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 376 (95% of vanilla; 9 runs, 376–376) vLLM-Lens: 155 (39% of vanilla; 9 runs, 147–158); significantly faster than the counterpart (p<0.05) nnsight eager: 145 (37% of vanilla; 9 runs, 142–148) nnsight taps (CUDA graphs): 372 (94% of vanilla; 9 runs, 371–372) generate, 8 concurrent interp-engine vllm: 1,102 (43% of vanilla; 9 runs, 1,056–1,125) interp-engine vllm-static: 2,405 (94% of vanilla; 9 runs, 2,399–2,413) vLLM-Lens: 1,148 (45% of vanilla; 9 runs, 1,123–1,172); significantly faster than the counterpart (p<0.05) nnsight eager: 1,090 (43% of vanilla; 9 runs, 1,058–1,116) nnsight taps (CUDA graphs): 2,421 (95% of vanilla; 9 runs, 2,417–2,425) capture 1 layer, every step interp-engine vllm: 143 (36% of vanilla; 9 runs, 139–147); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 370 (94% of vanilla; 9 runs, 366–371) vLLM-Lens: 135 (34% of vanilla; 9 runs, 132–138) nnsight eager: 135 (34% of vanilla; 9 runs, 131–137) nnsight taps (CUDA graphs): 366 (93% of vanilla; 9 runs, 366–368) capture every layer, every step interp-engine vllm: 102 (26% of vanilla; 9 runs, 99–104); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 295 (75% of vanilla; 9 runs, 288–302); significantly slower than the counterpart (p<0.05) vLLM-Lens: 96 (24% of vanilla; 9 runs, 94–98); significantly slower than the counterpart (p<0.05) nnsight eager: 119 (30% of vanilla; 9 runs, 117–121); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 361 (91% of vanilla; 9 runs, 360–362); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 1,032 (40% of vanilla; 9 runs, 999–1,050); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 2,339 (92% of vanilla; 9 runs, 2,332–2,345); significantly faster than the counterpart (p<0.05) vLLM-Lens: 882 (35% of vanilla; 9 runs, 859–900); significantly slower than the counterpart (p<0.05) nnsight eager: 940 (37% of vanilla; 9 runs, 924–958) nnsight taps (CUDA graphs): 2,224 (87% of vanilla; 9 runs, 2,209–2,229) additive steering, 1 layer interp-engine vllm: 145 (37% of vanilla; 9 runs, 140–148); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 370 (94% of vanilla; 9 runs, 370–371) vLLM-Lens: 116 (29% of vanilla; 9 runs, 115–119); significantly slower than the counterpart (p<0.05) nnsight eager: 134 (34% of vanilla; 9 runs, 131–136) nnsight taps (CUDA graphs): 365 (92% of vanilla; 9 runs, 362–366) logit lens every step interp-engine vllm: 90 (23% of vanilla; 9 runs, 88–92); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 145 (37% of vanilla; 9 runs, 138–148); significantly slower than the counterpart (p<0.05) vLLM-Lens: 109 (27% of vanilla; 9 runs, 106–111); significantly slower than the counterpart (p<0.05) nnsight eager: 134 (34% of vanilla; 9 runs, 133–136); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 325 (82% of vanilla; 9 runs, 324–325); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 143 (36% of vanilla; 9 runs, 132–147); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 367 (93% of vanilla; 9 runs, 364–368) vLLM-Lens: 112 (28% of vanilla; 9 runs, 110–115); significantly slower than the counterpart (p<0.05) nnsight eager: 134 (34% of vanilla; 9 runs, 132–136) nnsight taps (CUDA graphs): 364 (92% of vanilla; 9 runs, 363–364) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 135 (34% of vanilla; 9 runs, 134–137) nnsight taps (CUDA graphs): 367 (93% of vanilla; 9 runs, 366–368) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 136 (34% of vanilla; 9 runs, 132–138) nnsight taps (CUDA graphs): 365 (92% of vanilla; 9 runs, 364–366)
The numbers — Llama-3.2-1B, one GPU
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 395 145 372 150 376 155
generate, 8 concurrent 2,548 1,090 2,421 1,102 2,405 1,148
capture 1 layer, every step · 135 366 143 370 135
capture every layer, every step · 119 361 102 295 96
capture 1 layer, 8 concurrent · 940 2,224 1,032 2,339 882
additive steering, 1 layer · 134 365 145 370 116
logit lens every step · 134 325 90 145 109
linear probe every step · 134 364 143 367 112
zero one attention head every step · 135 367
override the sampled token every step · 136 365
Llama-3.1-8B, 2048-token prompt, 512 new tokens Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 85 (94% of vanilla; 9 runs, 84–85) interp-engine vllm-static: 87 (97% of vanilla; 9 runs, 87–87) vLLM-Lens: 85 (94% of vanilla; 9 runs, 83–85) nnsight eager: 84 (94% of vanilla; 9 runs, 83–85) nnsight taps (CUDA graphs): 87 (97% of vanilla; 9 runs, 87–87) generate, 8 concurrent interp-engine vllm: 537 (94% of vanilla; 9 runs, 537–538) interp-engine vllm-static: 553 (96% of vanilla; 9 runs, 552–553) vLLM-Lens: 539 (94% of vanilla; 9 runs, 537–541) nnsight eager: 539 (94% of vanilla; 9 runs, 538–541) nnsight taps (CUDA graphs): 558 (97% of vanilla; 9 runs, 557–558) capture 1 layer, every step interp-engine vllm: 84 (94% of vanilla; 9 runs, 83–84); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 87 (97% of vanilla; 9 runs, 86–87) vLLM-Lens: 76 (85% of vanilla; 9 runs, 74–78) nnsight eager: 77 (86% of vanilla; 9 runs, 74–78) nnsight taps (CUDA graphs): 87 (97% of vanilla; 9 runs, 87–87) capture every layer, every step interp-engine vllm: 48 (53% of vanilla; 9 runs, 47–50); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 67 (74% of vanilla; 9 runs, 63–70); significantly slower than the counterpart (p<0.05) vLLM-Lens: 38 (42% of vanilla; 9 runs, 37–38); significantly slower than the counterpart (p<0.05) nnsight eager: 66 (74% of vanilla; 9 runs, 64–67); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 86 (96% of vanilla; 9 runs, 86–86); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 528 (92% of vanilla; 9 runs, 526–532) interp-engine vllm-static: 544 (95% of vanilla; 9 runs, 542–546) vLLM-Lens: 468 (82% of vanilla; 9 runs, 461–473); significantly slower than the counterpart (p<0.05) nnsight eager: 530 (92% of vanilla; 9 runs, 523–534) nnsight taps (CUDA graphs): 553 (96% of vanilla; 9 runs, 552–553) additive steering, 1 layer interp-engine vllm: 84 (93% of vanilla; 9 runs, 83–84); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 87 (97% of vanilla; 9 runs, 86–87) vLLM-Lens: 50 (56% of vanilla; 9 runs, 50–50); significantly slower than the counterpart (p<0.05) nnsight eager: 75 (84% of vanilla; 9 runs, 75–77) nnsight taps (CUDA graphs): 86 (96% of vanilla; 9 runs, 86–87) logit lens every step interp-engine vllm: 65 (72% of vanilla; 9 runs, 64–65); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 64 (72% of vanilla; 9 runs, 63–66); significantly slower than the counterpart (p<0.05) vLLM-Lens: 48 (53% of vanilla; 9 runs, 46–48); significantly slower than the counterpart (p<0.05) nnsight eager: 77 (86% of vanilla; 9 runs, 76–77); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 82 (92% of vanilla; 9 runs, 82–82); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 83 (92% of vanilla; 9 runs, 81–84); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 87 (97% of vanilla; 9 runs, 86–87) vLLM-Lens: 49 (55% of vanilla; 9 runs, 48–50); significantly slower than the counterpart (p<0.05) nnsight eager: 75 (84% of vanilla; 9 runs, 74–76) nnsight taps (CUDA graphs): 86 (96% of vanilla; 9 runs, 86–86) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 77 (85% of vanilla; 9 runs, 75–78) nnsight taps (CUDA graphs): 87 (97% of vanilla; 9 runs, 86–87) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 76 (85% of vanilla; 9 runs, 74–78) nnsight taps (CUDA graphs): 87 (97% of vanilla; 9 runs, 86–87) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 1.35 s (56% of vanilla; 9 runs, 1.29 s–1.39 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.64 s (46% of vanilla; 9 runs, 1.60 s–1.70 s) nnsight taps (CUDA graphs): 1.54 s (49% of vanilla; 9 runs, 1.49 s–1.59 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 1.12 s (68% of vanilla; 9 runs, 1.09 s–1.15 s) nnsight taps (CUDA graphs): 1.03 s (73% of vanilla; 9 runs, 1.01 s–1.06 s)
The numbers — Llama-3.1-8B, 2048-token prompt, 512 new tokens
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 90 84 87 85 87 85
generate, 8 concurrent 574 539 558 537 553 539
capture 1 layer, every step · 77 87 84 87 76
capture every layer, every step · 66 86 48 67 38
capture 1 layer, 8 concurrent · 530 553 528 544 468
additive steering, 1 layer · 75 86 84 87 50
logit lens every step · 77 82 65 64 48
linear probe every step · 75 86 83 87 49
zero one attention head every step · 77 87
override the sampled token every step · 76 87
sweep: 1024 × 1 token, capture 1 layer, per request 0.76 s 1.64 s 1.54 s · · 1.35 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 1.12 s 1.03 s · ·
Llama-3.1-8B, 32 concurrent requests Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate, 32 concurrent interp-engine vllm: 1,430 (94% of vanilla; 9 runs, 1,426–1,434) interp-engine vllm-static: 1,456 (96% of vanilla; 9 runs, 1,452–1,461) vLLM-Lens: 1,455 (96% of vanilla; 9 runs, 1,446–1,461) nnsight eager: 1,434 (94% of vanilla; 9 runs, 1,433–1,436) nnsight taps (CUDA graphs): 1,469 (97% of vanilla; 9 runs, 1,465–1,479) capture 1 layer, 32 concurrent interp-engine vllm: 1,371 (90% of vanilla; 9 runs, 1,365–1,383); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 1,408 (92% of vanilla; 9 runs, 1,400–1,419); significantly faster than the counterpart (p<0.05) vLLM-Lens: 1,100 (72% of vanilla; 9 runs, 1,068–1,118); significantly slower than the counterpart (p<0.05) nnsight eager: 1,325 (87% of vanilla; 9 runs, 1,302–1,334) nnsight taps (CUDA graphs): 1,366 (90% of vanilla; 9 runs, 1,363–1,367)
The numbers — Llama-3.1-8B, 32 concurrent
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate, 32 concurrent 1,523 1,434 1,469 1,430 1,456 1,455
capture 1 layer, 32 concurrent · 1,325 1,366 1,371 1,408 1,100
DeepSeek-V2-Lite (MLA + MoE), one GPU Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate vLLM-Lens: 32 (19% of vanilla; 9 runs, 31–32) nnsight eager: 31 (19% of vanilla; 9 runs, 31–31) nnsight taps (CUDA graphs): 161 (98% of vanilla; 9 runs, 161–162) generate, 8 concurrent vLLM-Lens: 237 (27% of vanilla; 9 runs, 235–242) nnsight eager: 232 (27% of vanilla; 9 runs, 231–234) nnsight taps (CUDA graphs): 871 (100% of vanilla; 9 runs, 870–872) capture 1 layer, every step vLLM-Lens: 30 (18% of vanilla; 9 runs, 29–31); significantly faster than the counterpart (p<0.05) nnsight eager: 29 (18% of vanilla; 9 runs, 29–30) nnsight taps (CUDA graphs): 159 (97% of vanilla; 9 runs, 159–159) capture every layer, every step vLLM-Lens: 26 (16% of vanilla; 9 runs, 25–26); significantly slower than the counterpart (p<0.05) nnsight eager: 27 (17% of vanilla; 9 runs, 27–28); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 157 (96% of vanilla; 9 runs, 156–157) capture 1 layer, 8 concurrent vLLM-Lens: 215 (25% of vanilla; 9 runs, 213–217) nnsight eager: 213 (24% of vanilla; 9 runs, 209–216) nnsight taps (CUDA graphs): 810 (93% of vanilla; 9 runs, 805–812) additive steering, 1 layer vLLM-Lens: 28 (17% of vanilla; 9 runs, 28–28); significantly slower than the counterpart (p<0.05) nnsight eager: 29 (18% of vanilla; 9 runs, 29–29); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 158 (96% of vanilla; 9 runs, 158–158) logit lens every step vLLM-Lens: 27 (16% of vanilla; 9 runs, 26–27); significantly slower than the counterpart (p<0.05) nnsight eager: 29 (18% of vanilla; 9 runs, 29–29); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 152 (93% of vanilla; 9 runs, 152–152) linear probe every step vLLM-Lens: 27 (17% of vanilla; 9 runs, 27–28); significantly slower than the counterpart (p<0.05) nnsight eager: 29 (18% of vanilla; 9 runs, 28–29); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 157 (96% of vanilla; 9 runs, 157–157) zero one attention head every step vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 29 (18% of vanilla; 9 runs, 28–29) nnsight taps (CUDA graphs): o_proj is not tapped on this engine (taps carry layer outputs only) override the sampled token every step vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 29 (18% of vanilla; 9 runs, 28–30) nnsight taps (CUDA graphs): 159 (97% of vanilla; 9 runs, 158–159) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 0.92 s (54% of vanilla; 9 runs, 0.89 s–1.16 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.53 s (33% of vanilla; 9 runs, 1.51 s–1.55 s) nnsight taps (CUDA graphs): 1.42 s (35% of vanilla; 9 runs, 1.41 s–1.44 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 0.94 s (53% of vanilla; 9 runs, 0.79 s–1.27 s) nnsight taps (CUDA graphs): 0.71 s (71% of vanilla; 6 runs, 0.69 s–0.72 s)
The numbers — DeepSeek-V2-Lite, one GPU
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) vLLM-Lens
generate 164 31 161 32
generate, 8 concurrent 873 232 871 237
capture 1 layer, every step · 29 159 30
capture every layer, every step · 27 157 26
capture 1 layer, 8 concurrent · 213 810 215
additive steering, 1 layer · 29 158 28
logit lens every step · 29 152 27
linear probe every step · 29 157 27
zero one attention head every step · 29
override the sampled token every step · 29 159
sweep: 1024 × 1 token, capture 1 layer, per request 0.50 s 1.53 s 1.42 s 0.92 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 0.94 s 0.71 s
Qwen3.5-0.8B, one GPU Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 48 (12% of vanilla; 9 runs, 46–50) interp-engine vllm-static: 241 (57% of vanilla; 9 runs, 240–242); significantly slower than the counterpart (p<0.05) vLLM-Lens: 48 (11% of vanilla; 9 runs, 47–49) nnsight eager: 47 (11% of vanilla; 9 runs, 45–48) nnsight taps (CUDA graphs): 251 (60% of vanilla; 9 runs, 250–251); significantly faster than every counterpart (p<0.05) generate, 8 concurrent interp-engine vllm: 354 (14% of vanilla; 9 runs, 340–365) interp-engine vllm-static: 1,406 (56% of vanilla; 9 runs, 1,402–1,411); significantly slower than the counterpart (p<0.05) vLLM-Lens: 358 (14% of vanilla; 9 runs, 354–362); significantly faster than the counterpart (p<0.05) nnsight eager: 346 (14% of vanilla; 9 runs, 341–352) nnsight taps (CUDA graphs): 1,545 (61% of vanilla; 9 runs, 1,519–1,552); significantly faster than every counterpart (p<0.05) capture 1 layer, every step interp-engine vllm: 49 (12% of vanilla; 9 runs, 48–50); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 239 (57% of vanilla; 9 runs, 237–240) vLLM-Lens: 35 (8% of vanilla; 9 runs, 34–35); significantly slower than the counterpart (p<0.05) nnsight eager: 45 (11% of vanilla; 9 runs, 43–46) nnsight taps (CUDA graphs): 245 (58% of vanilla; 9 runs, 244–246) capture every layer, every step interp-engine vllm: 40 (10% of vanilla; 9 runs, 39–41) interp-engine vllm-static: 215 (51% of vanilla; 9 runs, 211–218); significantly slower than the counterpart (p<0.05) vLLM-Lens: 32 (8% of vanilla; 9 runs, 31–32); significantly slower than the counterpart (p<0.05) nnsight eager: 41 (10% of vanilla; 9 runs, 40–42) nnsight taps (CUDA graphs): 239 (57% of vanilla; 9 runs, 239–240); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 352 (14% of vanilla; 9 runs, 338–358); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 1,370 (54% of vanilla; 9 runs, 1,252–1,389) vLLM-Lens: 254 (10% of vanilla; 9 runs, 248–261); significantly slower than the counterpart (p<0.05) nnsight eager: 319 (13% of vanilla; 9 runs, 314–322) nnsight taps (CUDA graphs): 1,374 (54% of vanilla; 9 runs, 1,332–1,383) additive steering, 1 layer interp-engine vllm: 49 (12% of vanilla; 9 runs, 48–50); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 239 (57% of vanilla; 9 runs, 237–240) vLLM-Lens: 32 (8% of vanilla; 9 runs, 31–33); significantly slower than the counterpart (p<0.05) nnsight eager: 44 (11% of vanilla; 9 runs, 44–45) nnsight taps (CUDA graphs): 241 (57% of vanilla; 9 runs, 240–242) logit lens every step interp-engine vllm: 36 (8% of vanilla; 9 runs, 35–36); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 89 (21% of vanilla; 9 runs, 84–92); significantly slower than the counterpart (p<0.05) vLLM-Lens: 31 (7% of vanilla; 9 runs, 30–31); significantly slower than the counterpart (p<0.05) nnsight eager: 44 (11% of vanilla; 9 runs, 43–45); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 226 (54% of vanilla; 9 runs, 225–227); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 48 (11% of vanilla; 9 runs, 46–49); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 238 (57% of vanilla; 9 runs, 236–239) vLLM-Lens: 32 (8% of vanilla; 9 runs, 32–33); significantly slower than the counterpart (p<0.05) nnsight eager: 44 (11% of vanilla; 9 runs, 44–45) nnsight taps (CUDA graphs): 238 (57% of vanilla; 9 runs, 229–241) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 45 (11% of vanilla; 9 runs, 44–45) nnsight taps (CUDA graphs): o_proj is not tapped on this engine (taps carry layer outputs only) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 45 (11% of vanilla; 9 runs, 42–46) nnsight taps (CUDA graphs): 245 (58% of vanilla; 9 runs, 244–246) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 0.81 s (56% of vanilla; 9 runs, 0.78 s–0.85 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.30 s (35% of vanilla; 9 runs, 1.25 s–1.36 s) nnsight taps (CUDA graphs): 1.28 s (36% of vanilla; 9 runs, 1.24 s–1.33 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 0.91 s (50% of vanilla; 9 runs, 0.74 s–1.31 s) nnsight taps (CUDA graphs): 0.82 s (56% of vanilla; 9 runs, 0.73 s–1.09 s)
The numbers — Qwen3.5-0.8B, one GPU
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 420 47 251 48 241 48
generate, 8 concurrent 2,522 346 1,545 354 1,406 358
capture 1 layer, every step · 45 245 49 239 35
capture every layer, every step · 41 239 40 215 32
capture 1 layer, 8 concurrent · 319 1,374 352 1,370 254
additive steering, 1 layer · 44 241 49 239 32
logit lens every step · 44 226 36 89 31
linear probe every step · 44 238 48 238 32
zero one attention head every step · 45
override the sampled token every step · 45 245
sweep: 1024 × 1 token, capture 1 layer, per request 0.45 s 1.30 s 1.28 s · · 0.81 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 0.91 s 0.82 s · ·
Qwen3.5-4B, one GPU Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 37 (26% of vanilla; 9 runs, 36–37); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 103 (73% of vanilla; 9 runs, 103–103) vLLM-Lens: 36 (26% of vanilla; 9 runs, 35–37); significantly faster than the counterpart (p<0.05) nnsight eager: 35 (25% of vanilla; 9 runs, 34–36) nnsight taps (CUDA graphs): 106 (75% of vanilla; 9 runs, 105–106) generate, 8 concurrent interp-engine vllm: 274 (32% of vanilla; 9 runs, 270–276); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 627 (73% of vanilla; 9 runs, 626–628); significantly slower than the counterpart (p<0.05) vLLM-Lens: 270 (31% of vanilla; 9 runs, 262–276) nnsight eager: 263 (31% of vanilla; 9 runs, 254–268) nnsight taps (CUDA graphs): 648 (76% of vanilla; 9 runs, 642–653); significantly faster than every counterpart (p<0.05) capture 1 layer, every step interp-engine vllm: 36 (26% of vanilla; 9 runs, 36–37); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 102 (73% of vanilla; 9 runs, 101–103) vLLM-Lens: 26 (19% of vanilla; 9 runs, 25–26); significantly slower than the counterpart (p<0.05) nnsight eager: 33 (24% of vanilla; 9 runs, 32–34) nnsight taps (CUDA graphs): 104 (74% of vanilla; 9 runs, 104–104) capture every layer, every step interp-engine vllm: 28 (20% of vanilla; 9 runs, 27–28); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 87 (62% of vanilla; 9 runs, 84–88); significantly slower than the counterpart (p<0.05) vLLM-Lens: 22 (16% of vanilla; 9 runs, 21–22); significantly slower than the counterpart (p<0.05) nnsight eager: 31 (22% of vanilla; 9 runs, 30–31); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 103 (74% of vanilla; 9 runs, 102–103); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 263 (31% of vanilla; 9 runs, 257–268); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 623 (73% of vanilla; 9 runs, 618–631); significantly faster than the counterpart (p<0.05) vLLM-Lens: 193 (22% of vanilla; 9 runs, 187–197); significantly slower than the counterpart (p<0.05) nnsight eager: 242 (28% of vanilla; 9 runs, 237–246) nnsight taps (CUDA graphs): 604 (70% of vanilla; 9 runs, 602–605) additive steering, 1 layer interp-engine vllm: 36 (25% of vanilla; 9 runs, 35–36); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 102 (73% of vanilla; 9 runs, 102–103) vLLM-Lens: 24 (17% of vanilla; 9 runs, 24–25); significantly slower than the counterpart (p<0.05) nnsight eager: 33 (24% of vanilla; 9 runs, 33–34) nnsight taps (CUDA graphs): 104 (74% of vanilla; 9 runs, 104–104) logit lens every step interp-engine vllm: 28 (20% of vanilla; 9 runs, 28–29); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 60 (43% of vanilla; 9 runs, 58–61); significantly slower than the counterpart (p<0.05) vLLM-Lens: 23 (17% of vanilla; 9 runs, 23–24); significantly slower than the counterpart (p<0.05) nnsight eager: 33 (24% of vanilla; 9 runs, 32–34); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 97 (69% of vanilla; 9 runs, 96–97); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 36 (26% of vanilla; 9 runs, 35–36); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 102 (73% of vanilla; 9 runs, 99–102) vLLM-Lens: 24 (17% of vanilla; 9 runs, 24–24); significantly slower than the counterpart (p<0.05) nnsight eager: 33 (24% of vanilla; 9 runs, 33–34) nnsight taps (CUDA graphs): 104 (74% of vanilla; 9 runs, 103–104) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 33 (24% of vanilla; 9 runs, 32–34) nnsight taps (CUDA graphs): o_proj is not tapped on this engine (taps carry layer outputs only) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 33 (24% of vanilla; 9 runs, 33–34) nnsight taps (CUDA graphs): 104 (74% of vanilla; 9 runs, 103–104) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 1.59 s (60% of vanilla; 9 runs, 1.56 s–1.62 s); significantly faster than the counterpart (p<0.05) nnsight eager: 2.01 s (48% of vanilla; 9 runs, 1.98 s–2.05 s) nnsight taps (CUDA graphs): 2.01 s (48% of vanilla; 9 runs, 1.98 s–2.06 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 1.65 s (59% of vanilla; 9 runs, 1.47 s–2.01 s) nnsight taps (CUDA graphs): 1.49 s (65% of vanilla; 9 runs, 1.45 s–1.54 s)
The numbers — Qwen3.5-4B, one GPU
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 140 35 106 37 103 36
generate, 8 concurrent 857 263 648 274 627 270
capture 1 layer, every step · 33 104 36 102 26
capture every layer, every step · 31 103 28 87 22
capture 1 layer, 8 concurrent · 242 604 263 623 193
additive steering, 1 layer · 33 104 36 102 24
logit lens every step · 33 97 28 60 23
linear probe every step · 33 104 36 102 24
zero one attention head every step · 33
override the sampled token every step · 33 104
sweep: 1024 × 1 token, capture 1 layer, per request 0.96 s 2.01 s 2.01 s · · 1.59 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 1.65 s 1.49 s · ·
Qwen3.6-35B-A3B (mixture of experts), tensor-parallel 2 Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 16 (9% of vanilla; 9 runs, 15–16); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 108 (59% of vanilla; 9 runs, 106–108); significantly slower than the counterpart (p<0.05) vLLM-Lens: 15 (8% of vanilla; 9 runs, 15–16); significantly faster than the counterpart (p<0.05) nnsight eager: 15 (8% of vanilla; 9 runs, 15–15) nnsight taps (CUDA graphs): 119 (65% of vanilla; 9 runs, 118–119); significantly faster than every counterpart (p<0.05) generate, 8 concurrent interp-engine vllm: 115 (16% of vanilla; 9 runs, 113–117); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 491 (67% of vanilla; 9 runs, 479–500); significantly slower than the counterpart (p<0.05) vLLM-Lens: 117 (16% of vanilla; 9 runs, 115–118); significantly faster than the counterpart (p<0.05) nnsight eager: 112 (15% of vanilla; 9 runs, 109–115) nnsight taps (CUDA graphs): 545 (75% of vanilla; 9 runs, 530–557); significantly faster than every counterpart (p<0.05) capture 1 layer, every step interp-engine vllm: 16 (9% of vanilla; 9 runs, 15–16); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 107 (59% of vanilla; 9 runs, 107–107); significantly slower than the counterpart (p<0.05) vLLM-Lens: 13 (7% of vanilla; 9 runs, 13–13); significantly slower than the counterpart (p<0.05) nnsight eager: 14 (8% of vanilla; 9 runs, 14–15) nnsight taps (CUDA graphs): 116 (64% of vanilla; 9 runs, 115–116); significantly faster than every counterpart (p<0.05) capture every layer, every step interp-engine vllm: 13 (7% of vanilla; 9 runs, 13–13); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 74 (40% of vanilla; 9 runs, 72–76); significantly slower than the counterpart (p<0.05) vLLM-Lens: 12 (6% of vanilla; 9 runs, 11–12); significantly slower than the counterpart (p<0.05) nnsight eager: 14 (8% of vanilla; 9 runs, 13–14); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 114 (63% of vanilla; 9 runs, 114–114); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 116 (16% of vanilla; 9 runs, 115–117); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 492 (67% of vanilla; 9 runs, 487–498) vLLM-Lens: 95 (13% of vanilla; 9 runs, 92–97); significantly slower than the counterpart (p<0.05) nnsight eager: 105 (14% of vanilla; 9 runs, 102–106) nnsight taps (CUDA graphs): 487 (67% of vanilla; 9 runs, 483–491) additive steering, 1 layer interp-engine vllm: 16 (9% of vanilla; 9 runs, 16–16); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 107 (59% of vanilla; 9 runs, 107–107); significantly slower than the counterpart (p<0.05) vLLM-Lens: 12 (7% of vanilla; 9 runs, 12–13); significantly slower than the counterpart (p<0.05) nnsight eager: 14 (8% of vanilla; 9 runs, 14–15) nnsight taps (CUDA graphs): 115 (63% of vanilla; 9 runs, 112–116); significantly faster than every counterpart (p<0.05) logit lens every step interp-engine vllm: 12 (7% of vanilla; 9 runs, 12–12); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 38 (21% of vanilla; 9 runs, 37–38); significantly slower than the counterpart (p<0.05) vLLM-Lens: 12 (7% of vanilla; 9 runs, 12–12); significantly slower than the counterpart (p<0.05) nnsight eager: 14 (8% of vanilla; 9 runs, 14–14); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 111 (61% of vanilla; 9 runs, 110–112); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 15 (9% of vanilla; 9 runs, 15–16); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 106 (58% of vanilla; 9 runs, 102–107); significantly slower than the counterpart (p<0.05) vLLM-Lens: 12 (7% of vanilla; 9 runs, 12–13); significantly slower than the counterpart (p<0.05) nnsight eager: 14 (8% of vanilla; 9 runs, 14–15) nnsight taps (CUDA graphs): 115 (63% of vanilla; 9 runs, 114–115); significantly faster than every counterpart (p<0.05) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 14 (8% of vanilla; 9 runs, 14–15) nnsight taps (CUDA graphs): o_proj is not tapped on this engine (taps carry layer outputs only) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 14 (8% of vanilla; 9 runs, 14–15) nnsight taps (CUDA graphs): 116 (64% of vanilla; 9 runs, 114–116) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 1.62 s (50% of vanilla; 9 runs, 1.38 s–1.71 s); significantly faster than the counterpart (p<0.05) nnsight eager: 2.27 s (36% of vanilla; 9 runs, 1.87 s–2.54 s) nnsight taps (CUDA graphs): 2.11 s (39% of vanilla; 9 runs, 1.85 s–2.26 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 1.59 s (51% of vanilla; 9 runs, 1.30 s–1.70 s) nnsight taps (CUDA graphs): 1.52 s (54% of vanilla; 9 runs, 1.27 s–1.67 s)
The numbers — Qwen3.6-35B-A3B (MoE), tp=2
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 182 15 119 16 108 15
generate, 8 concurrent 732 112 545 115 491 117
capture 1 layer, every step · 14 116 16 107 13
capture every layer, every step · 14 114 13 74 12
capture 1 layer, 8 concurrent · 105 487 116 492 95
additive steering, 1 layer · 14 115 16 107 12
logit lens every step · 14 111 12 38 12
linear probe every step · 14 115 15 106 12
zero one attention head every step · 14
override the sampled token every step · 14 116
sweep: 1024 × 1 token, capture 1 layer, per request 0.82 s 2.27 s 2.11 s · · 1.62 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 1.59 s 1.52 s · ·

The three panels above are hybrid gated-delta-net trunks (Qwen3.5-0.8B: 18 of 24 layers are recurrent), and they needed a correctness fix before any taps number could be published: a full CUDA graph captured over such a trunk silently miscomputes prefill (plain vLLM does, with compilation off — the recurrent layers branch on the batch's prefill/decode composition), so a tapped nnsight engine now pins cudagraph_mode="FULL_DECODE_ONLY" on any model vLLM reports as hybrid or attention-free: prefill runs eagerly, decode keeps replay, and tapped generation matches eager exactly. Two consequences show in the numbers. The taps-to-vanilla gap is wider here (60–76%) than on standard trunks (93–97%), because vanilla's torch.compile genuinely pays on these new architectures and taps run without it — yet taps still lead every graph-mode alternative on these models. And the eager engines collapse hardest of any model measured (8–25% of vanilla, identically for all three libraries): a recurrent layer is many small kernels, each a Python round trip, and vanilla is very fast. The head-ablation row is ✗ under taps here because only layer outputs are tapped on these trunks.

Llama-3.1-8B, tensor-parallel 8 Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 72 (23% of vanilla; 9 runs, 70–73); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 289 (92% of vanilla; 9 runs, 288–290) vLLM-Lens: 74 (24% of vanilla; 9 runs, 71–75); significantly faster than the counterpart (p<0.05) nnsight eager: 69 (22% of vanilla; 9 runs, 68–70) nnsight taps (CUDA graphs): 286 (91% of vanilla; 9 runs, 278–290) generate, 8 concurrent interp-engine vllm: 511 (30% of vanilla; 9 runs, 496–524); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 1,540 (91% of vanilla; 9 runs, 1,514–1,554) vLLM-Lens: 525 (31% of vanilla; 9 runs, 516–533); significantly faster than the counterpart (p<0.05) nnsight eager: 496 (29% of vanilla; 9 runs, 469–507) nnsight taps (CUDA graphs): 1,568 (93% of vanilla; 9 runs, 1,557–1,581) capture 1 layer, every step interp-engine vllm: 68 (22% of vanilla; 9 runs, 67–70); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 251 (80% of vanilla; 9 runs, 250–253); significantly slower than the counterpart (p<0.05) vLLM-Lens: 65 (21% of vanilla; 9 runs, 64–68) nnsight eager: 64 (20% of vanilla; 9 runs, 63–65) nnsight taps (CUDA graphs): 284 (91% of vanilla; 9 runs, 282–285); significantly faster than every counterpart (p<0.05) capture every layer, every step interp-engine vllm: 22 (7% of vanilla; 9 runs, 20–24); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 36 (12% of vanilla; 9 runs, 30–40); significantly slower than the counterpart (p<0.05) vLLM-Lens: 36 (11% of vanilla; 9 runs, 34–37); significantly slower than the counterpart (p<0.05) nnsight eager: 57 (18% of vanilla; 9 runs, 56–57); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 276 (88% of vanilla; 9 runs, 275–277); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 440 (26% of vanilla; 9 runs, 426–452) interp-engine vllm-static: 1,082 (64% of vanilla; 9 runs, 993–1,120); significantly slower than the counterpart (p<0.05) vLLM-Lens: 415 (25% of vanilla; 9 runs, 404–423); significantly slower than the counterpart (p<0.05) nnsight eager: 442 (26% of vanilla; 9 runs, 432–450) nnsight taps (CUDA graphs): 1,443 (85% of vanilla; 9 runs, 1,435–1,451); significantly faster than every counterpart (p<0.05) additive steering, 1 layer interp-engine vllm: 70 (22% of vanilla; 9 runs, 68–71); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 251 (80% of vanilla; 9 runs, 250–253); significantly slower than the counterpart (p<0.05) vLLM-Lens: 57 (18% of vanilla; 9 runs, 56–58); significantly slower than the counterpart (p<0.05) nnsight eager: 64 (20% of vanilla; 9 runs, 63–65) nnsight taps (CUDA graphs): 278 (89% of vanilla; 9 runs, 276–279); significantly faster than every counterpart (p<0.05) logit lens every step interp-engine vllm: 23 (7% of vanilla; 9 runs, 22–24); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 30 (10% of vanilla; 9 runs, 19–33); significantly slower than the counterpart (p<0.05) vLLM-Lens: 52 (17% of vanilla; 9 runs, 50–54); significantly slower than the counterpart (p<0.05) nnsight eager: 63 (20% of vanilla; 9 runs, 62–65); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 275 (88% of vanilla; 9 runs, 273–276); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 68 (22% of vanilla; 9 runs, 65–70); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 245 (78% of vanilla; 9 runs, 236–249); significantly slower than the counterpart (p<0.05) vLLM-Lens: 56 (18% of vanilla; 9 runs, 55–56); significantly slower than the counterpart (p<0.05) nnsight eager: 64 (20% of vanilla; 9 runs, 63–65) nnsight taps (CUDA graphs): 276 (88% of vanilla; 9 runs, 273–277); significantly faster than every counterpart (p<0.05) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 64 (20% of vanilla; 9 runs, 62–64) nnsight taps (CUDA graphs): o_proj is not tapped on this engine (taps carry layer outputs only) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 65 (21% of vanilla; 9 runs, 64–66) nnsight taps (CUDA graphs): 284 (91% of vanilla; 9 runs, 283–285) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 1.02 s (25% of vanilla; 9 runs, 0.91 s–1.20 s); significantly faster than the counterpart (p<0.05) nnsight eager: 1.21 s (21% of vanilla; 9 runs, 1.15 s–1.31 s) nnsight taps (CUDA graphs): 1.18 s (21% of vanilla; 9 runs, 1.12 s–1.26 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 2.24 s (11% of vanilla; 9 runs, 2.01 s–2.54 s) nnsight taps (CUDA graphs): 2.21 s (11% of vanilla; 9 runs, 2.01 s–2.53 s)
The numbers — Llama-3.1-8B, tp=8
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 313 69 286 72 289 74
generate, 8 concurrent 1,689 496 1,568 511 1,540 525
capture 1 layer, every step · 64 284 68 251 65
capture every layer, every step · 57 276 22 36 36
capture 1 layer, 8 concurrent · 442 1,443 440 1,082 415
additive steering, 1 layer · 64 278 70 251 57
logit lens every step · 63 275 23 30 52
linear probe every step · 64 276 68 245 56
zero one attention head every step · 64
override the sampled token every step · 65 284
sweep: 1024 × 1 token, capture 1 layer, per request 0.25 s 1.21 s 1.18 s · · 1.02 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 2.24 s 2.21 s · ·
Llama-3.1-70B, tensor-parallel 8 Throughput as a share of plain vLLM doing the same generation with nothing attached (100% = vanilla). Hollow = eager, filled = CUDA graphs. Whisker = min–max over runs. nnsight dots are smaller and in front, so a tie shows both. ✗ = cannot express. Hover a dot for the number, its run count and range, and whether it is significantly faster than its nnsight counterpart (exact Mann-Whitney U, p<0.05, ≥3% apart). nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens 25% 50% 75% 100% vanilla vLLM generate interp-engine vllm: 32 (53% of vanilla; 6 runs, 32–33); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 59 (97% of vanilla; 6 runs, 59–59) vLLM-Lens: 32 (53% of vanilla; 9 runs, 32–33); significantly faster than the counterpart (p<0.05) nnsight eager: 30 (50% of vanilla; 6 runs, 30–31) nnsight taps (CUDA graphs): 59 (96% of vanilla; 9 runs, 59–59) generate, 8 concurrent interp-engine vllm: 218 (62% of vanilla; 6 runs, 215–219); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 334 (95% of vanilla; 6 runs, 332–336) vLLM-Lens: 219 (63% of vanilla; 9 runs, 218–221); significantly faster than the counterpart (p<0.05) nnsight eager: 202 (57% of vanilla; 6 runs, 188–209) nnsight taps (CUDA graphs): 331 (94% of vanilla; 9 runs, 308–338) capture 1 layer, every step interp-engine vllm: 31 (50% of vanilla; 6 runs, 30–31); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 55 (90% of vanilla; 6 runs, 55–56); significantly slower than the counterpart (p<0.05) vLLM-Lens: 29 (47% of vanilla; 9 runs, 28–30); significantly faster than the counterpart (p<0.05) nnsight eager: 28 (45% of vanilla; 6 runs, 27–28) nnsight taps (CUDA graphs): 58 (96% of vanilla; 6 runs, 58–58); significantly faster than every counterpart (p<0.05) capture every layer, every step interp-engine vllm: 5 (9% of vanilla; 6 runs, 5–6); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 7 (12% of vanilla; 6 runs, 6–8); significantly slower than the counterpart (p<0.05) vLLM-Lens: 10 (17% of vanilla; 9 runs, 10–11); significantly slower than the counterpart (p<0.05) nnsight eager: 24 (39% of vanilla; 6 runs, 23–24); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 57 (94% of vanilla; 6 runs, 57–58); significantly faster than every counterpart (p<0.05) capture 1 layer, 8 concurrent interp-engine vllm: 189 (54% of vanilla; 6 runs, 186–192) interp-engine vllm-static: 279 (80% of vanilla; 6 runs, 276–282); significantly slower than the counterpart (p<0.05) vLLM-Lens: 173 (49% of vanilla; 9 runs, 153–179); significantly slower than the counterpart (p<0.05) nnsight eager: 185 (53% of vanilla; 6 runs, 181–188) nnsight taps (CUDA graphs): 323 (92% of vanilla; 9 runs, 321–325); significantly faster than every counterpart (p<0.05) additive steering, 1 layer interp-engine vllm: 31 (51% of vanilla; 6 runs, 31–31); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 56 (91% of vanilla; 6 runs, 55–56); significantly slower than the counterpart (p<0.05) vLLM-Lens: 21 (34% of vanilla; 9 runs, 17–22); significantly slower than the counterpart (p<0.05) nnsight eager: 27 (44% of vanilla; 6 runs, 26–28) nnsight taps (CUDA graphs): 58 (95% of vanilla; 9 runs, 58–58); significantly faster than every counterpart (p<0.05) logit lens every step interp-engine vllm: 16 (26% of vanilla; 6 runs, 16–16); significantly slower than the counterpart (p<0.05) interp-engine vllm-static: 21 (35% of vanilla; 6 runs, 21–22); significantly slower than the counterpart (p<0.05) vLLM-Lens: 21 (34% of vanilla; 9 runs, 20–21); significantly slower than the counterpart (p<0.05) nnsight eager: 28 (45% of vanilla; 6 runs, 28–28); significantly faster than every counterpart (p<0.05) nnsight taps (CUDA graphs): 58 (95% of vanilla; 6 runs, 58–58); significantly faster than every counterpart (p<0.05) linear probe every step interp-engine vllm: 30 (50% of vanilla; 6 runs, 30–31); significantly faster than the counterpart (p<0.05) interp-engine vllm-static: 56 (91% of vanilla; 6 runs, 55–56); significantly slower than the counterpart (p<0.05) vLLM-Lens: 22 (36% of vanilla; 6 runs, 22–22); significantly slower than the counterpart (p<0.05) nnsight eager: 28 (46% of vanilla; 6 runs, 27–29) nnsight taps (CUDA graphs): 58 (95% of vanilla; 6 runs, 58–58); significantly faster than every counterpart (p<0.05) zero one attention head every step interp-engine vllm: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points interp-engine vllm-static: no write to z / o_proj input: steering ops are add, orthogonal, projection_cap at residual points vLLM-Lens: hooks fire on decoder-layer outputs (and inputs) only; o_proj's input is not a hook point nnsight eager: 28 (46% of vanilla; 6 runs, 27–29) nnsight taps (CUDA graphs): o_proj is not tapped on this engine (taps carry layer outputs only) override the sampled token every step interp-engine vllm: no hook on sampling: generation is the engine's own interp-engine vllm-static: no hook on sampling: generation is the engine's own vLLM-Lens: no hook on sampling: generation is the engine's own nnsight eager: 28 (47% of vanilla; 6 runs, 28–29) nnsight taps (CUDA graphs): 58 (96% of vanilla; 6 runs, 58–58) sweep: 1024 × 1 token, capture 1 layer, per request vLLM-Lens: 5.70 s (55% of vanilla; 9 runs, 5.16 s–6.38 s) nnsight eager: 5.51 s (57% of vanilla; 6 runs, 5.44 s–5.61 s) nnsight taps (CUDA graphs): 5.12 s (62% of vanilla; 9 runs, 3.43 s–5.56 s) sweep: 1024 × 1 token, capture 1 layer, edit() once vLLM-Lens: no persistent-block equivalent for capture; persistent hooks cover the hook path nnsight eager: 6.24 s (51% of vanilla; 6 runs, 5.91 s–6.77 s) nnsight taps (CUDA graphs): 5.70 s (55% of vanilla; 9 runs, 5.33 s–6.29 s)
The numbers — Llama-3.1-70B, tp=8
workload vanilla vLLM nnsight eager nnsight taps (CUDA graphs) interp-engine vllm interp-engine vllm-static vLLM-Lens
generate 61 30 59 32 59 32
generate, 8 concurrent 351 202 331 218 334 219
capture 1 layer, every step · 28 58 31 55 29
capture every layer, every step · 24 57 5 7 10
capture 1 layer, 8 concurrent · 185 323 189 279 173
additive steering, 1 layer · 27 58 31 56 21
logit lens every step · 28 58 16 21 21
linear probe every step · 28 58 30 56 22
zero one attention head every step · 28
override the sampled token every step · 28 58
sweep: 1024 × 1 token, capture 1 layer, per request 3.16 s 5.51 s 5.12 s · · 5.70 s
sweep: 1024 × 1 token, capture 1 layer, edit() once · 6.24 s 5.70 s · ·

The two panels above extend the tensor-parallel series to all eight cards (three trials for 8B, two for 70B, on a shared node — runs that overlapped another user's job were dropped, and the trials that remain agree within 3%). The trend from tp=2 and tp=4 simply continues. Plain vLLM keeps scaling (8B: 92 → 148 → 229 → 313 tok/s from one to eight cards; 70B: 37 → 61 from four to eight), and the graph engines follow it: capturing a layer every step, nnsight taps hold 91% of vanilla on 8B and 95% on 70B, interp-engine static 80% and 90%. The eager engines do not move at all — nnsight eager, interp-engine's hooked engine and vLLM-Lens sit at 64–74 tok/s on 8B at every card count, which is now 20–24% of vanilla, and at 28–32 on 70B (46–53%; the heavier step hides more of the per-module handoff). Where the libraries differ is what they serve under graphs: every-layer capture on 70B is 57 tok/s under taps against 5–10 for the three engines that gather the tensors out of the worker. One number goes the other way: the edit()-once sweep, the fastest capture on one GPU, gets slower with every rank added while the per-request trace does not — on 8B, 1.05 s at tp=2, 1.35–1.46 s at tp=4, 2.2 s at tp=8, against a per-request trace steady at 1.2–1.4 s (70B/tp=8: 5.7 against 5.1). An installed block runs its saves on every rank; a per-request one collects once. Past four cards, trace the sweep.

The harness (ie-bench/: one runner per library, common.py for the rows, report.py and the chart script), the raw results-*.jsonl and every log live alongside nnsight's tests; every number above was produced by it.