main
py 112 lines 4.31 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """CPU DeepSeek-V4 compressor subclass and dispatch: partial-state-cache
4 write and the compress -> RMSNorm -> RoPE -> FP8 quant -> KV cache store
5 step, calling ``torch.ops._C.save_partial_states_cpu``/
6 ``compress_norm_rope_store_cpu``/``compress_norm_rope_store_indexer_cpu``
7 (``csrc/cpu/sgl-kernels/compressor.cpp``) in place of triton-cpu.
8
9 Covers both the head_dim=512 main-attention compressor path and the
10 head_dim=128 indexer compressor path (fp8 only -- MXFP4, ``use_fp4_cache``,
11 never occurs on CPU: the indexer's MXFP4 cache requires a Blackwell GPU).
12 """
13
14 from typing import Any, cast
15
16 import torch
17
18 from vllm._custom_ops import (
19 compress_norm_rope_store_cpu,
20 compress_norm_rope_store_indexer_cpu,
21 save_partial_states_cpu,
22 )
23 from vllm.forward_context import get_forward_context
24 from vllm.models.deepseek_v4.compressor import CompressorMetadata, DeepseekCompressor
25 from vllm.models.deepseek_v4.cpu.cpu_utils import map_local_to_global_slots_cpu
26
27
28 class DeepseekV4CPUCompressor(DeepseekCompressor):
29 """CPU compressor: same state/weights as the shared base, but
30 ``forward``'s cache-write and compress->RMSNorm->RoPE->quant->store
31 steps always dispatch straight to the ported CPU kernels above instead
32 of through the shared method's platform-dispatch chain. This class
33 only ever runs on CPU, and ``use_fp4_cache`` is always ``False`` here
34 (the indexer's MXFP4 cache requires a Blackwell GPU), so the shared
35 method's MXFP4/two-stage/cutedsl/triton branches never apply.
36 """
37
38 _norm_weight_fp32: torch.Tensor
39
40 def cache_norm_weight_fp32(self) -> None:
41 """Cache a fp32 contiguous copy of ``norm.weight`` once, right after
42 weight loading (see
43 ``DeepseekV4CPUAttention.process_weights_after_loading``) -- the CPU
44 kernel requires fp32/contiguous but the checkpoint loads this weight
45 in bf16, and it never changes after loading."""
46 self._norm_weight_fp32 = self.norm.weight.to(torch.float32).contiguous()
47
48 def forward(
49 self,
50 kv_score: torch.Tensor,
51 positions: torch.Tensor,
52 rotary_emb,
53 ) -> None:
54 kv, score = kv_score.split(
55 [self.coff * self.head_dim, self.coff * self.head_dim], dim=-1
56 )
57
58 attn_metadata = get_forward_context().attn_metadata
59 if not isinstance(attn_metadata, dict):
60 return
61
62 state_metadata = attn_metadata[self.state_cache.prefix]
63 assert isinstance(state_metadata, CompressorMetadata)
64 token_to_req_indices = state_metadata.token_to_req_indices
65 assert token_to_req_indices is not None
66 slot_mapping = state_metadata.slot_mapping
67 num_actual = slot_mapping.shape[0]
68 block_table = state_metadata.block_table
69 block_size = state_metadata.block_size
70
71 state_cache = self.state_cache.kv_cache
72
73 save_partial_states_cpu(
74 kv, score, self.ape, positions, state_cache, slot_mapping
75 )
76
77 cos_sin_cache = rotary_emb.cos_sin_cache
78 k_cache_metadata = cast(Any, attn_metadata[self.k_cache_prefix])
79 k_cache_layer = self._static_forward_context[self.k_cache_prefix]
80 kv_cache = k_cache_layer.kv_cache
81
82 assert self.head_dim in (512, 128), (
83 f"unsupported compressor head_dim: {self.head_dim}"
84 )
85 window = (1 + self.overlap) * self.compress_ratio
86 win_offsets = torch.arange(window, device=positions.device)
87 win_local = positions.unsqueeze(-1) - window + 1 + win_offsets
88 req_idx = token_to_req_indices[:num_actual]
89 gather_slots = map_local_to_global_slots_cpu(
90 win_local, req_idx, block_table, block_size
91 )
92
93 kv_cache_2d = kv_cache.view(kv_cache.shape[0], -1)
94 kv_cache_block_size = kv_cache.shape[1]
95
96 store_fn = (
97 compress_norm_rope_store_cpu
98 if self.head_dim == 512
99 else compress_norm_rope_store_indexer_cpu
100 )
101 store_fn(
102 state_cache,
103 gather_slots,
104 positions,
105 k_cache_metadata.slot_mapping,
106 self._norm_weight_fp32,
107 self.rms_norm_eps,
108 cos_sin_cache,
109 kv_cache_2d,
110 kv_cache_block_size,
111 self.compress_ratio,
112 )