← models

V100 Builds · released 2026-10-21

DeepSeek-V4.1-Flash-1017 EXL3

DeepSeek-V4.1-Flash at its native 1M context on 8× V100. EXL3 experts, KL 0.0358 vs the fp32 reference.

native context
1M native context64K / 256K / 512K needles pass
KL vs fp32
0.0358 KL vs fp32DeepSeek's native reference, 95.79% top-1
tok/s prefill
2,985–3,231 tok/s prefillsolo, 8K to 128K context
tok/s decode
64.9 tok/s decodesingle stream, code, DSpark on

DeepSeek-V4.1-Flash-1017 is a 384-expert MoE with a 1M-token context, Engram lookup tables and a built-in DSpark drafter. The official checkpoint is 510 GB of FP8 dense weights and MXFP4 experts. That does not fit 256 GB of Volta. The only SM70 support was an open 1Cat-vLLM PR that ran it eager, experts spilled to host RAM, at 3.5-3.9 tok/s.

I built an EXL3 expert pack that fits, picked per expert from public packs with nothing re-quantized, and ported what the engine needed to serve it: EXL3 MoE kernels, graph-captured attention over a packed FP4 KV cache, Engram from host RAM, DSpark, then a day of prefill work. It runs the full 1,048,576-token context on one 8× V100-SXM2-32GB box.

Fidelity is measured against DeepSeek's own reference code run in fp32 on CPU: KL 0.0358 and 95.79% top-1 on the current build. Solo prefill is 2,985-3,231 tok/s from 8K to 128K. With DSpark speculative decoding, a single stream decodes code at 64.9 tok/s and chat at 35.0, and two streams reach 95.5 tok/s on code. In my own day-to-day coding sessions on the live server it averages about 80 tok/s. It also takes images: the vision tower runs from host RAM, so the 1M context is unchanged.

runs on
8× V100Tesla V100 32GB
engine
1Cat-vLLM (SM70) · 1Cat-vLLM 1.5.1 + PR #1031 @ a36df7c
stack
  • 1Cat-vLLM (SM70), PR #1243 on #1031
  • EXL3 routed experts (exllamav3 trellis, mul1 codebook, K2-K5 per expert)
  • PP2×TP4, experts parallel inside each TP group
  • Packed FP4 compressed KV cache
  • DSpark speculative decoding (k=5)
  • Fused, graph-captured decode indexer
  • Vision tower streamed from host RAM (image input)
  • CUDA 12.8, Triton and WMMA kernels

01

Research log

  1. Start

    First time running DeepSeek locally. 1Cat 1.5.1 supports V4-Flash, not V4.1; V4.1 lives in open PR #1031, which is 1.5.1 plus 5 commits. Pulled the 510 GB official checkpoint and built the SM70 tree against CUDA 12.8.

  2. First tokens: 2.82 tok/s

    Served the official MXFP4 weights with experts spilled to host RAM, 32K context, one sequence. Chat and a tool call were correct; decode was 2.82 tok/s. Spilling the least-routed experts first took it to 4.07.

  3. Graphs, staging, 1M

    Found CUDA graphs had been silently disabled; PIECEWISE graphs doubled decode to 8.53 tok/s. Staging spilled experts into HBM gave 10.20. Moving to DeepSeek's 1M context cost speed (9.02); a bit-exact fused Sinkhorn kernel got it to 11.85.

  4. EXL3 experts in VRAM

    Wrote the mul1 codebook, K5 and per-expert-K paths for the SM70 EXL3 MoE kernel and loaded 0xSero's 2.7 bpw pack expert-parallel. No more spill: 16.54 tok/s, TTFT about 0.2 s instead of 2.4 s, at 256K context.

  5. Packed FP4 KV, native 1M

    Stored the compressed KV packed instead of as fp16. Greedy output stayed byte-identical and the KV pool reached 1,427,403 tokens, so 1M fit. A 533,739-token needle passed.

  6. 30.1 tok/s, exact

    Fused hyper-connection kernels, split-K decode attention, per-layer graphs with device-side builders, async scheduling, deferred zero-copy Engram and a 1530 MHz clock lock. 30.1 tok/s single stream at KL 0.0000 vs the exact kernels, 74.0 aggregate at four streams.

  7. CPU oracle and the franken pack

    Built an fp32 CPU oracle from DeepSeek's reference code and measured packs against it: 0xSero 0.0663, HeNryous 0.0421, franken 0.0364. Same decode speed for all three. Fixed the DSpark PP bookkeeping and drafter graph bugs, and trimmed the stage-2 budget to 22.0 GiB so the drafter and 1M KV fit together.

  8. Published

    Batch-invariant GEMM buckets, and DSpark at two sequences once the drafter's all-reduces moved to NCCL. Uploaded franken-v100 to Hugging Face and opened PR #1243 against 1Cat-vLLM.

  9. Memory that stays put

    Every OOM so far was memory allocated after vLLM's profiler ran. Added demand-mapped KV through CUDA VMM, one shared pool for the decode graphs and fail-fast workers. A 30-minute agent-style soak passed with 70+ completions and no errors.

  10. Prefill set

    Exact decoder replay (layers 21-39 only on each request's last 3,072 rows), fused WMMA prefill attention at 1.45 ms per layer instead of 9.46, a tiled EXL3 prefill MoE kernel and Engram lookahead. Solo prefill went from 527 / 626 / 617 to 989 / 1,636 / 2,274 tok/s at 8K / 32K / 128K, KL 0.0358.

  11. Engram in RAM

    Moved the Engram tables from a ZFS dataset into hugepage RAM, split across both NUMA nodes. Solo prefill reached 2,186 / 2,841 / 2,376 tok/s.

  12. Layer-sliced prefill

    A long prefill now runs in slices of a few layers between decode steps. A stream decoding during a 32K prefill went from 6.0 to 52.6 tok/s, with sliced and unsliced prefill bit-identical.

  13. Fused indexer kernels

    Fused kernels for indexer candidate scoring, scan-and-mask and top-512. Solo prefill 2,985 / 3,231 / 3,194 tok/s, 56.9 tok/s for a stream decoding through a 32K prefill. Oracle KL unchanged at 0.0358.

  14. DSpark fix and fused decode, current build

    Scaled the DSpark taps before the fp16 cast so attention-sink tokens no longer overflow, and made the decode indexer a fused, graph-capturable kernel. Single-stream code decode went from 43.8 to 64.9 tok/s, and decode at 128K context from 28 to 53. KL unchanged at 0.0358.

  15. Image input

    Turned on the vision tower and aligner, which the port had skipped, with the weights in host RAM so the 1M context stays. Image tokens route through the MoE gate's vision bias and skip Engram, as in DeepSeek's reference. Also fixed a crash when a prefix-cache hit landed on a staged prefill. KL unchanged at 0.0358; 6 / 6 image tests answered correctly.

built on the work of

  • 0xSero: his 2.7 bpw pack was the first DeepSeek-V4.1 pack that ran on 8× V100 and the baseline for two days of engine work. The first franken plan borrowed 204 of his K4 experts.
  • HeNryous and the sudoingX lineage: the base pack and its sensitivity-informed bit allocation.
  • Mia-AiLab: the EAQ 2.9 bpw pack the upgrades come from.
  • 1CatAI: the SM70 vLLM fork that makes Volta a real target. PLI Labs' PR #1031 brought DeepSeek-V4.1 up on it.
  • DeepSeek: the model, the reference inference code the oracle is built on, and DeepInfer, where the drafter-graph shape, batch invariance and the self-consistency gate came from.

02

Get it running

you'll need

  • 8× Tesla V100-SXM2-32GB with NVLink, used as two quads of four (PP2×TP4, cut at layer 20).
  • Host RAM for the Engram tables. The current build loads them RAM-resident, 45.78 GiB per stage-0 rank, and refuses to start unless the tables plus 32 GiB are available. My box has 372 GB and 64 cores.
  • Disk: 218.6 GB for the pack, plus the two official Engram shards (101.5 GB each). Put the Engram shards on a small record size (ZFS recordsize=16K here): Engram reads 256-byte rows at random.
  • CUDA 12.8, Python 3.12, torch 2.10 cu128. Passwordless sudo lets the launcher lock SM clocks at 1530 MHz and set the CPU governor to performance.
  1. 1

    Check the hardware

    You need 8× V100-SXM2-32GB on NVLink, two quads of four. Stage 1 takes 27.77 GiB of weights per GPU and stage 2 25.29 GiB plus the drafter, so 16 GB cards are out. Plan for host RAM above the Engram tables plus 32 GiB, and disk for 218.6 GB of pack plus 203 GB of Engram shards.

    shell
    nvidia-smi topo -m && free -g
  2. 2

    Download the pack and the Engram shards

    The pack repo holds the EXL3 experts and every official non-expert tensor. The Engram tables are unmodified, so they come from DeepSeek's repo: download shards 47 and 48 into the same directory. Then run verify_engram.sh from the engine repo: it checks both shards against the Hub's sha256 and writes the .sha256-ok sidecars the launcher's preflight wants.

    shell
    hf download philbert440/DeepSeek-V4.1-Flash-1017-EXL3-franken-v100 --revision 99d85a463b446e4ff07e173be6a8faf9b920b97d --local-dir ds41-franken-v100
    hf download deepseek-ai/DeepSeek-V4.1-Flash-1017 model-00047-of-00048.safetensors model-00048-of-00048.safetensors --revision 1bae0e1088993b9b82de08f59de5be5a34e42170 --local-dir ds41-franken-v100
    DS41_MODEL=$PWD/ds41-franken-v100 deepseek-v4.1-v100/v100/verify_engram.sh

    Model cardBase model

  3. 3

    Install the engine

    Install the prebuilt SM70 wheel from the deepseek-v4.1-v100 release: the engine as served on 2026-10-25, image input included, with the three prebuilt DS41 extensions bundled. You still need the CUDA 12.8 toolkit, a C++ compiler and ninja at runtime, because the Engram reader and the TileLang kernels compile on first start. To build from source instead, clone the tag and follow the repo README. PR #1243 is the engine as of 2026-10-21, without the later prefill, decode and DSpark fixes.

    shell
    git clone https://github.com/philbert440/deepseek-v4.1-v100
    # wheel + SHA256SUMS from the ds41-vision-20261025b release
    sha256sum -c SHA256SUMS
    uv venv --python 3.12 .venv
    uv pip install --python .venv/bin/python ./1cat_vllm-1.5.2.dev1149+ds41vision1025b-cp312-cp312-linux_x86_64.whl ninja --torch-backend=cu128

    Release ds41-vision-20261025b (wheel)deepseek-v4.1-v100: build from sourcePR #1243 (2026-10-21 engine)

  4. 4

    Launch

    v100/serve_ds41.sh is the live configuration: PP2×TP4 cut at layer 20, --max-model-len 1048576, --max-num-batched-tokens 1024, --gpu-memory-utilization 0.943, DSpark with 5 draft tokens, --max-num-seqs 2, and every VLLM_DS41_* knob the published numbers were measured with. Copy the env example and set your paths and GPU quads, then dry-run: preflight checks that the GPUs are free, each quad is all-NVLink, and the shards, port and memory are in place, without launching. DS41_DSPARK=0 switches to plain decode. With passwordless sudo the launcher sets the CPU governor and locks SM clocks at 1530 MHz. With Engram loaded into RAM, a boot takes about 12 minutes.

    shell
    cd deepseek-v4.1-v100
    cp v100/ds41.env.example ds41.env   # DS41_MODEL, DS41_PYTHON, DS41_GPUS_A / DS41_GPUS_C
    source ds41.env
    v100/serve_ds41.sh --dry-run --strict
    v100/serve_ds41.sh   # detached tmux session; --foreground to stay attached

    serve_ds41.shds41.env.exampleHF recipe (2026-10-21 launcher)

  5. 5

    Smoke test

    Served model name is deepseek-v4.1-flash, on DS41_PORT from ds41.env. Healthy looks like /health 200, 391 for the prompt below, and a get_weather call with {"city": "Toronto"} and finish_reason tool_calls from v100/smoke.py, which also reports streamed decode tok/s. Thinking is off by default; pass chat_template_kwargs: {"thinking": true} and it arrives in the reasoning field. Images go in as OpenAI image_url content parts (data URLs work), also inside tool results, up to 64 per prompt; each image becomes up to 1,024 prompt tokens.

    shell
    curl -s http://127.0.0.1:$DS41_PORT/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"deepseek-v4.1-flash","messages":[{"role":"user","content":"What is 17 * 23? Answer with just the number."}],"max_tokens":400,"temperature":0}'

    v100/smoke.py

03

Downloads

VariantFormatRuns onSizeDL / 30d
franken-v100, ~2.8 bpw routed experts
philbert440/DeepSeek-V4.1-Flash-1017-EXL3-franken-v100
EXL3 ~2.8 bpw routed experts + official FP8 / BF16 / MXFP4 dense and drafter8× V100218.6 GB (+ 203 GB official Engram shards)186

04

Inference stack

PR #1031 (PLI Labs) is the V4.1 bring-up on 1Cat 1.5.1. PR #1243 stacks on it and carries the engine as of 2026-10-21: EXL3 MoE kernels, graph-captured decode attention, fused hyper-connection kernels, the Engram host service, DSpark, batch-invariant GEMMs.

The current build adds decoder replay, fused prefill attention, a tiled EXL3 prefill MoE kernel, RAM-resident Engram, layer-sliced prefill, fused indexer kernels, a graph-capturable fused decode indexer, the DSpark fp16 tap fix, a sliced-prefill fix for prefix-cache hits and image input. None of that is in PR #1243; deepseek-v4.1-v100 carries it.

deepseek-v4.1-v100 is the engine as served on 2026-10-25: #1031's tree imported unmodified, then the engine squashed on top. Its tree equals engine commit 714597f124 except for publication edits (host names in comments, extension and bench-script default paths, the Engram reader also finding its sources inside the package, internal gate drivers removed). No kernel or model logic differs. The two engine commits after it, the sliced-prefill fix and image input (engine commit 71c3e6e14f), are replayed as they are. The release ds41-vision-20261025b carries the current prebuilt SM70 wheel (up to 64 images per prompt); ds41-final-20261025 is the text-only one.

Every addition sits behind a VLLM_DS41_* knob. Each one was gated against the fp32 oracle before it went live.

CUDA 13 dropped Volta, so the stack stays on CUDA 12.8.

upstream PRs carried · 1CatAI/1Cat-vLLM

  • #1243[Model][SM70] DeepSeek-V4.1-Flash-1017 on 8x V100: EXL3 experts, graph attention, DSpark speculative decoding, 1M context (stacks on #1031)

05

Recipe

5.1The pack: per-expert selection, nothing re-quantized

The base is HeNryous' 2.75 bpw pack (sudoingX lineage, sensitivity-informed per-expert bits, native-1017 calibration). It measured best of the public packs on my oracle panel.

frankenplan.py upgrades experts to the next wider K available (K2→K3) from Mia-AiLab's EAQ 2.9 bpw pack, in descending routing frequency, until each pipeline stage's byte budget is used. Budgets per GPU are 24.37 GiB on stage 1 (layers 0-19) and 22.0 GiB on stage 2 (layers 20-39). Stage 2 is tighter because it also holds the DSpark drafter and the 1M-token KV cache. A 23.0 GiB stage-2 variant scored the same KL (0.036) but only fits plain decode.

Routing frequencies come from histograms over a mixed workload (route_workload.py). The first franken plan also borrowed 204 K4 experts from 0xSero's pack; the published one fills its budget from HeNryous and Mia alone.

Dense, attention, Engram, head and the three MTP blocks stay as the official FP8 / BF16 / MXFP4 tensors. make_franken_hybrid.py assembles symlinks plus a merged index and plan, and repack_upload.py wrote the published shards byte-exact from the donors.

5.2The engine port

PP2×TP4, one pipeline stage per NVLink quad. DeepSeek's 2304-wide experts don't split across 4 ranks at the kernel's 128-column Hadamard blocks, so experts run expert-parallel inside each TP group: each rank owns whole experts, balanced by routing histogram. No new collective, since the MoE output was already all-reduced.

The EXL3 trellis is decoded inside the MoE GEMV, nothing dequantized to memory: mul1 codebook, K2-K5 per expert from one flat buffer. A grouped kernel decodes each expert once per ≤8 routed slots and takes over from 2 rows. Dense weights run FP16 with FP32 accumulation and an exact norm chain.

The compressed KV is stored packed, FP4 E2M1 plus an E4M3 scale per 16 values, 288 B per latent. Storage is lossless (greedy output byte-identical to the fp16 cache), and it is what made 1M fit. Engram's 256-byte rows were first served from disk through a deferred zero-copy path; the current build holds the tables in hugepage RAM and the gather is a memcpy.

Decode runs per-layer CUDA graphs with device-side metadata builders; the drafter has its own plain graphs per batch size. Prefill got decoder replay (layers 21-39 run only each request's last 3,072 rows, exact), fused WMMA sparse attention, a tiled EXL3 prefill MoE kernel, PP prefill pipelining, layer-sliced prefill between decode steps, and fused indexer kernels for candidate scoring, scan-and-mask and top-512.

5.3How fidelity is measured

V100 has no FP8, so there is no GPU reference to compare against. oracle_cpu.py runs DeepSeek's own inference/model.py on CPU in fp32, with every tilelang kernel replaced by exact torch math: act_quant with power-of-2 scales, FP4 ties-to-even, FP8/FP4 GEMMs as dequant plus fp32 matmul, sparse attention with sink, Sinkhorn.

The panel is 8 domains, 3,134 positions, prompt plus the model's own greedy reply. I report KL on the reference's top-20 support, top-1 agreement and the NLL gap. The panel is small and writing-heavy and reads about 2× the KL the pack cards report on their own panels, so compare packs with each other.

Every engine change also has to pass self-consistency (step-wise decode vs one-pass prefill of the same tokens, mean KL ≤ 0.05), batched-vs-single exactness, bit-identity where slicing changes the schedule, needles, the prefix cache gate and a tool-call smoke. Perf A/Bs ran on a loopback-only port with the access log checked. Numbers taken while other clients hit the server were thrown out.

06

Results

Decode with DSpark, current build

Natural chat-template prompts, temperature 0, stopping at the model's own EOS, 1M context, DSpark k=5. Two streams is the aggregate. AL is tokens produced per verify step, the bonus token included. replay_bench's short completion-style prompt, which has no BOS, decodes at 69.5 tok/s steady; it never hit the fp16 tap bug, which is why the old 69.2 headline looked fine. In real use the number is higher: my own agentic coding sessions through dsh on the live server average about 80 tok/s, Over those sessions the server logged an AL of 4.3, the same as the code benchmark and nearly twice chat's.

Content1 streamAL2 streamsPrevious build, 1 stream
Code64.9 tok/s4.3895.5 tok/s43.8 tok/s
Structured JSON65.9 tok/s4.35103.3 tok/s44.4 tok/s
Chat35.0 tok/s2.2757.3 tok/s29.1 tok/s
Prose34.5 tok/s2.2552.3 tok/s26.3 tok/s

measured on: My 8× V100-SXM2-32GB box, franken-v100, current build (2026-10-25), loopback-only server

Image input, current build

Synthetic test images with known content, OpenAI image_url parts, temperature 0. Time is the whole request. The vision tower's output is within 1.5e-2 relative L2 of an fp32 run of DeepSeek's reference tower (the reference's own bf16 path: 2.7e-2). Text gates on the same build: KL 0.0358, self-consistency worst mean 0.0237, KV pool 1,055,580 tokens.

ImagePrompt tokensAnswerTime
Red circle, 800×600340A red circle.1.1 s
Text "VOLTA 4417", 800×600330VOLTA 44170.8 s
Five blue rectangles, 800×60033650.8 s
Text "GERTY 2400", 2400×1600991GERTY 24005.3 s
Screenshot inside a tool result635PORT 80411.2 s
Three images in one prompt980VOLTA 4417 / PORT 8041 / circle2.2 s

measured on: My 8× V100-SXM2-32GB box, franken-v100, current build with image input (2026-10-25)

Pack comparison vs the fp32 native reference

8 domains, 3,134 positions, top-20 support. Plain decode, no DSpark. The franken tok/s were measured on the first franken plan (with 0xSero K4 experts, 23.0 GiB stage-2 budget); the published pack's KL row is its own.

PackRouted bpwKLTop-1NLL gap1 stream4 streams
0xSero 2.7 bpw2.700.06694.2%+0.05230.68 tok/s59.7 tok/s
HeNryous 2.75 bpw2.750.04295.5%+0.02830.56 tok/s71.7 tok/s
franken-v100~2.80.036595.6%+0.01930.56 tok/s63.0 tok/s

measured on: My 8× V100-SXM2-32GB box, PP2×TP4, 1Cat-vLLM SM70 port as of 2026-10-20

franken-v100 KL by domain

Same oracle panel. Writing is the weak spot for every pack I measured.

DomainKLTop-1Positions
Code0.011398.82%847
Math0.014196.99%432
Agentic0.028495.00%440
Multilingual0.037297.48%318
Knowledge0.039694.50%418
Chat0.065093.52%432
Writing0.111388.66%247
All0.035895.79%3,134

measured on: My 8× V100-SXM2-32GB box, published pack, current build (2026-10-25)

Solo prefill by prompt length, 2026-10-24

replay_bench.py: prompt tokens / TTFT, max_tokens 1, unique random prefix per request so the prefix cache cannot hit. Each row adds to the one above.

Build8K32K128K
Start of day (all prefill knobs off)527626617
+ replay, fused attention, tiled MoE, Engram lookahead9891,6362,274
+ RAM-resident Engram2,1862,8412,376
+ layer-sliced prefill2,2052,8192,370
Current: + fused indexer kernels2,9853,2313,194

measured on: My 8× V100-SXM2-32GB box, franken-v100, 1M context, DSpark on, loopback-only server; successive builds of 2026-10-24

A 32K prefill while another stream decodes

replay_bench.py hol: one greedy stream decoding, then a 32K prompt arrives. tok/s of each side while both run. The current slicer gives the faster decoder more of each step. A 35K prompt arriving mid-decode on the current build prefills at 1,550 tok/s while the resident stream keeps 38.9 tok/s (43.1 alone); the longest stall is 0.38 s.

BuildPrefill tok/sDecoder tok/s during prefill
Start of day6962.04
RAM-resident Engram, unsliced2,2636.01
Layer-sliced prefill1,39852.6
Fused indexer kernels (2026-10-24)1,61656.9
Current (2026-10-25)1,46056.0

measured on: My 8× V100-SXM2-32GB box, franken-v100, 1M context, DSpark on, loopback-only server; builds of 2026-10-24 and 2026-10-25

Decode at long context

lc_decode.py, DSpark on. No cliff with context: decode at 128K matches 8K. The fp16 tap fix doubled it; with only the fused decode indexer, the same night's numbers were 24.4 and 28.3 tok/s.

ContextDecode tok/sPrefill tok/s on the same promptTime to first token
8K (8,192 tokens)51.02,5763.2 s
128K (131,072 tokens)52.63,20740.9 s

measured on: My 8× V100-SXM2-32GB box, franken-v100, current build (2026-10-25)

Single-stream decode, bring-up to first usable server

Plain decode, no DSpark. First two rows are smoke.py's 256-token stream; the rest are bench_decode.py medians.

BuildContextDecode tok/s
#1031 as-is: MXFP4, experts spilled to host, eager32K2.82
+ least-routed experts spilled first32K4.07
+ CUDA graphs actually on32K8.53
+ spilled experts staged to HBM32K10.20
+ fused Sinkhorn, at 1M1M11.85
EXL3 experts in VRAM (0xSero pack), no spill256K16.54
+ packed FP4 KV1M16.72
+ graph attention, device builders, exact norm1M26.74
+ async scheduling, deferred Engram, 1530 MHz lock1M30.1

measured on: My 8× V100-SXM2-32GB box, PP2×TP4, successive 1Cat-vLLM SM70 builds, 2026-10-18 to 2026-10-19

Gates
GateResultBuild
KL vs fp32 native reference0.0358, top-1 95.79% (3,134 positions)Current
Self-consistency (decode vs prefill)PASS, worst mean KL 0.0237Current
Sliced vs unsliced prefillBit-identical, 0 / 56 mismatchesCurrent, 512K
KV pool at 1M context1,055,580 tokensCurrent
Tool call (get_weather)Correct call, finish_reason tool_callsCurrent
Image answers6 / 6 correct, incl. 2400×1600, a tool-result image and three images in one promptCurrent
Prefix-cache hit during a staged prefillPASS (55K cached + 3.9K new while another stream decodes)Current
Fused indexer GPU tests8 / 8 (two diffs are near-ties)Current
Needle 64K / 256K at the 1M configPASS, 599 / 558 tok/s prefill2026-10-20, DSpark
Needle 512K (533,739 tokens)PASS, 418 tok/s prefill2026-10-19, 0xSero pack
Batched vs single greedyx2 6/6, x3 9/9 identical2026-10-21 default
Prefix cache, 16K prompt16 s cold → 1 s hit, same continuation2026-10-21 default

measured on: My 8× V100-SXM2-32GB box, franken-v100; the build is named per row

07

Findings

  • CUDA graphs were silently off at the start. The engine asked for FULL_AND_PIECEWISE, the attention backend reported NEVER, and vLLM fell back to no graphs. Forcing PIECEWISE took decode from 4.04 to 8.53 tok/s with byte-identical output.
  • The EXL3 trellis decode is ALU-bound on V100, not bandwidth-bound. MoE cost scales with unique experts touched per step. Split-K bought -18% at one route per rank and nothing at two or more; a persistent stream-K decode kernel came out at 0.55-0.85x of the grouped kernel.
  • DSpark with the eager proposer was net-negative: 18.7 vs 30.5 tok/s plain, with normal acceptance. The drafter graphs gave 11% acceptance until I found that each drafter layer has its own KV group and block table. The B>1 crash turned out to be vLLM's custom all-reduce inside the drafter graph; running only the drafter's all-reduces on NCCL fixed it.
  • DSpark drafts were all zeros for the first ~100 steps of every chat request. The drafter's taps were captured in fp16, and the attention-sink tokens at the start of a chat (BOS, the user tag, the first word) overflow 65,504. The resulting NaNs sat in the drafter's 128-token window until those positions scrolled out. Completion-style prompts have no BOS, so benchmarks never showed it. Scaling the taps down before the fp16 cast fixed it: steps with NaN drafts went to zero, first-draft acceptance on code went from 0.49 to 0.90, and single-stream code decode from 43.8 to 64.9 tok/s.
  • The same bug explains an earlier ceiling. First-draft acceptance had topped out near 0.49 even on easy content and came in streaks, 0.20 after a step that accepted nothing and 0.67-0.91 after one that accepted something. A trace had ruled out an off-by-one in the PP bookkeeping. It was the NaN window: while it was in place, nothing the drafter proposed could be accepted.
  • Batched decode flipped tokens that single-request decode picked by a 0.19-nat margin, by 0.2-0.7 nats per copy. Cause: cuBLAS picks its kernel by row count, amplified through 40 MoE layers at 2.8 bpw. Padding GEMM rows to fixed buckets (≤16 → 16, else multiples of 64) took mixed-step KL from 0.12 to 0.013 at no measured speed cost.
  • The port's hidden-state drift vs the reference was the 2.7 bpw experts, not missing activation quantization. Per-layer cosine fell smoothly from 0.999 at layer 0 to 0.87 by layer 30. An oracle without activation QAT matched the port worse (0.875 vs 0.898).
  • Capping prefill tokens per step was a poor fix for head-of-line blocking. On a cold-Engram bench, a 128-token cap lifted the co-running decoder from 0.8 to 3.20 tok/s and cut prefill from 480 to 307 tok/s. Chunk cost is mostly fixed: a 1024-token chunk already touches 86 of 94 local experts. Layer-sliced prefill is what fixed it.
  • A decode-priority lane on its own was rejected: no decoder gain, prefill down 25-30%. Prefill chunks of 2048 did not fit 1M; the window and state groups cap context at about 791K there.
  • Engram on a 16K-record ZFS dataset capped cold reads at about 2.0 s per chunk's 49K records, regardless of thread count. Holding the tables in RAM cut a 1024-token chunk's gather to 0.9 ms from about 400 ms of cold pread per rank.
  • Binding workers to their NUMA node changed nothing measurable (4.04 vs 4.03 tok/s). Locking SM clocks at 1530 MHz did: 26.90 to 27.68 tok/s median, minimum 23.05 to 26.92, because auto-boost dipped between latency-bound decode bursts.
  • The checkpoint always had a vision tower; the V100 port just skipped it. Running it without costing context meant keeping its 926 MiB of weights in pinned host RAM and streaming them through two small GPU buffers per image, because the 1M KV pool leaves about 8 MB free per GPU. One surprise: PyTorch attention takes the math path on Volta and builds the full score matrix, 4.2 GiB for a large image. Chunking the queries keeps it near 128 MB with bit-identical output.
  • prompt_logprobs on long prompts runs out of memory in the 1M DSpark configuration. There is no slack for full-vocabulary chunk logits.