- serve/: the minimal OpenAI-compatible server of deepseek-ai/DeepInfer#147, with a Neuron worker setup (basic mode env, per-rank NeuronCore, fixed KV pool via --num-kv-blocks, no MTP lookahead) and a /v1/completions endpoint (prompt text or token ids, no chat template): what vllm bench serve --backend openai drives, as vllm-neuron#71's run_bench.py does. - examples/neuron/bench_neuron.py: the same TTFT/TPOT matrix offline. - static MoE: one packed fp32 all-gather instead of three (each 64-rank collective is ~2 ms of fixed latency a layer); a capacity-grouped expert path for prefill chunks (DEEPINFER_NEURON_MOE_CAPACITY, dense rerun on overflow; not yet validated on device). - Sub-region timings at split() boundaries under DEEPINFER_NEURON_REGION_TIMING. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.4 KiB
Serving DeepInfer + host prefix cache (local additions, not part of upstream)
Pieces
serve/openai_server.py— OpenAI-compatible/v1/chat/completions(streaming,tools, usage) on top of the offline engine: one engine process per GPU (attention DP + MoE EP), prompts rendered with the checkpoint's ownencoding.py.serve/server.sh start|stopruns it as a process group (stop reaps the engine helpers, which otherwise keep the GPUs).deepinfer/hicache/— host-memory prefix cache (SGLang HiCache style): pinned host pools organized in pages (one page = one 128-token KV block) plus SWA state slots, hash-chain index, LRU eviction, write-through at the end of every prefill, "kernel" transfers (a CUDA kernel copying pages through UVA pointers on the engine stream,page_copy.cu, JIT-built on first use). Needs the read-onlySequence.block_tablebinding added incsrc/engine/pybind_sequence.cpp(rebuilddeepinfer._Cwithbash scripts/build.sh build).tests/hicache/test_host_cache.py— cache logic against a fake engine (needs one GPU).serve/check_prefix_cache.py— A/B on a live server: each turn sent with"prefix_cache": falseand with the cache, greedy; compares outputs and TTFT.
Run
export DEEPINFER_ACCELERATOR=nvidia DEEPINFER_HF_MODEL_DIR=/path/to/DeepSeek-V4.1-Flash-1017
serve/server.sh start --dp 4 --port 8888 # cache on: 32 GiB pages + 4096 states per rank
serve/server.sh start --dp 4 --port 8888 --hicache-size-gb 0 # cache off
python serve/check_prefix_cache.py --port 8888 --turns 3
curl -s localhost:8888/deepinfer/metrics | grep hicache # per-rank hit/store counters
serve/server.sh stop
Knobs: --hicache-size-gb, --hicache-state-slots, --routing affinity|least-loaded (affinity keeps a
conversation's turns on one rank, keyed by the first user message; required for hits since the cache is
per rank). The cache requires decoder bounded replay (--yoco-keep-tokens > 0, the default 140).
EPLB expert rearrangement is off by default (--eplb-interval 0). Every rearrangement stalls all ranks for about
0.3 s, and with --eplb-after-prefill 1 it fires after nearly every step under multi-turn traffic; enable it with
--eplb-interval 30 only for decode-heavy workloads whose expert load is skewed.
Prompt tokenization
Agentic clients resend the whole conversation every turn, and tokenizer.encode needs ~1.7 ms per 1K tokens on a
GB300 Grace core (p50 278 ms per request on replayed AgentX prompts of p50 163K tokens), which dominates TTFT once the
prefix cache hits. serve/prompt_tokenizer.py gives the same ids faster; --tokenizer-backend picks one:
fastokens(pip install fastokens; x86-64 and aarch64 wheels): Rust reimplementation of the HF pipeline, built from the loaded tokenizer. 3.5 ms p50 on the same prompts.segment(no dependency): splits the prompt at added tokens, where HF tokenizers already encode the pieces independently, and caches each piece's ids (LRU, 512 MiB), so a turn only encodes what it adds. 18 ms p50. Enabled only when the tokenizer config makes the split exact (nosingle_wordtokens, noprepend_scheme="first").hf:tokenizer.encode.
auto (default) takes the first of these that matches tokenizer.encode on startup probes built from the
tokenizer's added tokens. --tokenizer-verify-rate (default 0.005) re-encodes a sample of prompts with
tokenizer.encode in the background; a mismatch switches to hf. The backend, encode latency and check counters are
in /deepinfer/metrics (deepinfer_tokenizer:*). Tests: tests/serve/test_prompt_tokenizer.py.
How a hit works
A new prompt's page hashes are matched from the start; the match is capped so that at least keep_tokens
tokens remain to be recomputed (the decoder SWA state is rebuilt from that suffix by bounded replay), then
snapped down to the nearest 128-aligned position that has a stored SWA state. The sequence is allocated up
front, pages and state are copied in on the engine stream, num_cached_tokens is set, and the sequence is
enqueued; the scheduler prefills only the suffix. Generated tokens are not stored at decode end (they are
stored when they come back as part of the next turn's prompt).
AgentX
The server speaks what InferenceX's AgentX replay (aiperf, --endpoint /v1/chat/completions --streaming --use-server-token-count) needs; prefill-any-interval matters there, since a single lane keeps most ranks idle.