Skip to content

vllm: reject float16 and fp8 KV caches at startup - #157

Open
samadarsh wants to merge 2 commits into
generative-computing:mainfrom
samadarsh:claude/dreamy-bardeen-aovoj2
Open

samadarsh wants to merge 2 commits into
generative-computing:mainfrom
samadarsh:claude/dreamy-bardeen-aovoj2

Conversation

@samadarsh

Copy link
Copy Markdown
Contributor

Summary

The vLLM MultiSwitch stores a -1e9 counting mask and integer adapter ids in the paged KV cache. With a float16 or fp8 cache they don't survive, and adapter routing goes silently wrong while the base model keeps generating. docs/MULTISWITCH_EXPLAINED.md §8 listed --kv-cache-dtype fp8 as unguarded; this adds the guard.

Measured on CPU by rebuilding both heads and round-tripping K/V through each dtype (bf16 model):

KV storage result
bf16 / fp32 correct
float16, fp8 e5m2 mask → -inf, 0 * -inf = NaN, every write address wrong
fp8 e4m3 correct up to 16 adapters; ids 17+ round (54/565 tokens misrouted at 20 adapters)
--dtype float16 mask overflows or fills -inf, depending on the code path

Changes

  • vllm/switch/_kv_dtype.py (new, vLLM-free): check_switch_kv_dtype accepts only bf16/fp32 storage and raises a ValueError naming the flag to change.
  • vllm/switch/multi.py: MultiSwitch.__init__ calls it before building the attention heads.
  • tests/unit/test_switch_kv_dtype.py (new, CPU): accepted vs rejected configs, plus the arithmetic the guard rests on.
  • docs/MULTISWITCH_EXPLAINED.md §8, CLAUDE.md gotcha 10: measured causes. The old text blamed the memory keys (code(n) * 28), but codeword entries are ±1/8, so those keys peak at 3.5 and fit in fp8.

Judgment call: fp8 e4m3 with ≤16 adapters passes the CPU simulation but is still rejected, because it hasn't been validated under vLLM's fp8 attention backends. Happy to relax it if you'd prefer.

Testing

  • New unit test passes; CPU unit suite passes with the CI marker filter.
  • Not run on a GPU (I don't have one). The guard only reads cache_config.cache_dtype at construction, with a safe fallback if the attribute is absent; the vLLM GPU suites use the bf16 default, so it shouldn't trip them. The published granite-switch-4.1-*-preview checkpoints are saved as bf16, so they pass the guard with default settings.

The vLLM MultiSwitch heads store a -1e9 counting mask and integer adapter
ids in the paged KV cache. float16 and fp8 e5m2 turn the mask into -inf, so
the one-hot query computes 0 * -inf = NaN and every write address is wrong;
fp8 e4m3 rounds adapter ids above 16 to a neighbour. In both cases routing
is silently wrong while the base model keeps generating. --dtype float16
fails too: the mask is built in the model dtype, where it either overflows
or fills -inf depending on the code path.

MultiSwitch.__init__ now calls check_switch_kv_dtype, which accepts only
bfloat16 or float32 storage (what the switch has been validated on) and
raises a ValueError naming the flag to change. The check lives in a
vLLM-free module, _kv_dtype.py, so tests/unit/test_switch_kv_dtype.py runs
it on CPU along with the arithmetic it rests on.

docs/MULTISWITCH_EXPLAINED.md section 8 listed fp8 as unguarded and gave
the memory keys (code(n) * 28) as a cause; codeword entries are +/-1/8, so
those keys peak at 3.5 and fit in fp8. The section now gives the measured
causes and documents the check; CLAUDE.md gotcha 10 follows.

Signed-off-by: samadarsh <samadarsh14@gmail.com>
Bring the fp8/float16 KV-cache guard up to date with main (vLLM 0.26-0.30 range, generative-computing#144 docs cleanup, SWITCH kernels write-up). No conflicts.

Signed-off-by: samadarsh <samadarsh14@gmail.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants