Skip to content

Commit f1178f3

Browse files
WoosukKwoncodex
andauthored
Revert DSv4 eager workspace reuse (#52836)
Signed-off-by: Woosuk Kwon <woosuk@inferact.ai> Co-authored-by: OpenAI Codex <codex@openai.com>
1 parent a9f4afb commit f1178f3

15 files changed

Lines changed: 30 additions & 354 deletions

csrc/libtorch_stable/fused_deepseek_v4_qnorm_rope_kv_insert_kernel.cu

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -942,10 +942,9 @@ static void launchFullCacheKernel(
942942
// ────────────────────────────────────────────────────────────────────────────
943943
// Torch op wrapper
944944
// ────────────────────────────────────────────────────────────────────────────
945-
void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
945+
torch::stable::Tensor fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert(
946946
torch::stable::Tensor const& q_in, // [N, num_heads_q, 512] bf16
947947
torch::stable::Tensor const& kv, // [N, 512] bf16 (read-only)
948-
torch::stable::Tensor& q_out, // [N, q_head_padded, 512]
949948
torch::stable::Tensor& k_cache, // [num_blocks, block_bytes] uint8
950949
torch::stable::Tensor const& slot_mapping, // [N] int64
951950
torch::stable::Tensor const& position_ids, // [N] int64
@@ -971,16 +970,8 @@ void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
971970
STD_TORCH_CHECK(kv.dim() == 2 && kv.size(1) == 512, "kv shape [N, 512]");
972971
STD_TORCH_CHECK(q_in.scalar_type() == kv.scalar_type(),
973972
"q_in and kv dtype must match");
974-
STD_TORCH_CHECK(q_out.device() == q_in.device() && q_out.is_contiguous(),
975-
"q_out must be contiguous and on the same device as q_in");
976-
STD_TORCH_CHECK(q_out.scalar_type() == q_in.scalar_type(),
977-
"q_out dtype must match q_in");
978973
STD_TORCH_CHECK(q_head_padded >= q_in.size(1),
979974
"q_head_padded must be >= q_in.size(1) (num_heads_q)");
980-
STD_TORCH_CHECK(q_out.dim() == 3 && q_out.size(0) == q_in.size(0) &&
981-
q_out.size(1) == q_head_padded &&
982-
q_out.size(2) == q_in.size(2),
983-
"q_out shape [N, q_head_padded, 512]");
984975
STD_TORCH_CHECK(k_cache.scalar_type() == torch::headeronly::ScalarType::Byte,
985976
"k_cache must be uint8");
986977
STD_TORCH_CHECK(cos_sin_cache.dim() == 2 && cos_sin_cache.size(1) == 64,
@@ -1008,6 +999,11 @@ void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
1008999
q_in.get_device_index());
10091000
const cudaStream_t stream = get_current_cuda_stream(q_in.get_device_index());
10101001

1002+
// Allocate the padded q output. The kernel writes every element (live
1003+
// region gets RMSNorm+RoPE; pad region gets zeros), so `empty` is safe.
1004+
auto q_out = torch::stable::new_empty(
1005+
q_in, {q_in.size(0), q_head_padded, q_in.size(2)}, q_in.scalar_type());
1006+
10111007
VLLM_STABLE_DISPATCH_HALF_TYPES(
10121008
q_in.scalar_type(), "fused_deepseek_v4_qnorm_rope_kv_insert", [&] {
10131009
using qkv_scalar_t = scalar_t;
@@ -1024,20 +1020,6 @@ void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
10241020
num_heads_q_padded, cache_block_size_i, kv_block_stride,
10251021
stream);
10261022
});
1027-
}
1028-
1029-
torch::stable::Tensor fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert(
1030-
torch::stable::Tensor const& q_in, torch::stable::Tensor const& kv,
1031-
torch::stable::Tensor& k_cache,
1032-
torch::stable::Tensor const& slot_mapping,
1033-
torch::stable::Tensor const& position_ids,
1034-
torch::stable::Tensor const& cos_sin_cache, int64_t q_head_padded,
1035-
double eps, int64_t cache_block_size) {
1036-
auto q_out = torch::stable::new_empty(
1037-
q_in, {q_in.size(0), q_head_padded, q_in.size(2)}, q_in.scalar_type());
1038-
fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
1039-
q_in, kv, q_out, k_cache, slot_mapping, position_ids, cos_sin_cache,
1040-
q_head_padded, eps, cache_block_size);
10411023
return q_out;
10421024
}
10431025

csrc/libtorch_stable/ops.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -269,14 +269,6 @@ torch::stable::Tensor fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert(
269269
torch::stable::Tensor const& cos_sin_cache, int64_t q_head_padded,
270270
double eps, int64_t cache_block_size);
271271

272-
void fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
273-
torch::stable::Tensor const& q_in, torch::stable::Tensor const& kv,
274-
torch::stable::Tensor& q_out, torch::stable::Tensor& k_cache,
275-
torch::stable::Tensor const& slot_mapping,
276-
torch::stable::Tensor const& position_ids,
277-
torch::stable::Tensor const& cos_sin_cache, int64_t q_head_padded,
278-
double eps, int64_t cache_block_size);
279-
280272
void fused_deepseek_v4_qnorm_rope_kv_rope_full_cache_bf16_insert(
281273
torch::stable::Tensor& q, torch::stable::Tensor const& kv,
282274
torch::stable::Tensor& k_cache, torch::stable::Tensor const& slot_mapping,

csrc/libtorch_stable/torch_bindings.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -433,11 +433,6 @@ STABLE_TORCH_LIBRARY_FRAGMENT(_C, ops) {
433433
"Tensor q_in, Tensor kv, Tensor! k_cache, "
434434
"Tensor slot_mapping, Tensor position_ids, Tensor cos_sin_cache, "
435435
"int q_head_padded, float eps, int cache_block_size) -> Tensor");
436-
ops.def(
437-
"fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out("
438-
"Tensor q_in, Tensor kv, Tensor! q_out, Tensor! k_cache, "
439-
"Tensor slot_mapping, Tensor position_ids, Tensor cos_sin_cache, "
440-
"int q_head_padded, float eps, int cache_block_size) -> ()");
441436

442437
// FlashInfer V4 full-cache variants: write Q in place (bf16) or to a separate
443438
// FP8 tensor, and KV into a contiguous 512-wide token-strided cache.
@@ -773,8 +768,6 @@ STABLE_TORCH_LIBRARY_IMPL(_C, CUDA, ops) {
773768
ops.impl("fused_qk_norm_rope", TORCH_BOX(&fused_qk_norm_rope));
774769
ops.impl("fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert",
775770
TORCH_BOX(&fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert));
776-
ops.impl("fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out",
777-
TORCH_BOX(&fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out));
778771
ops.impl(
779772
"fused_deepseek_v4_qnorm_rope_kv_rope_full_cache_bf16_insert",
780773
TORCH_BOX(&fused_deepseek_v4_qnorm_rope_kv_rope_full_cache_bf16_insert));

tests/kernels/test_compressor_kv_cache.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
from vllm import _custom_ops as ops
2222
from vllm.models.deepseek_v4.common.ops import (
23-
compute_global_topk_indices_and_lens,
2423
dequantize_and_gather_k_cache,
2524
quantize_and_insert_k_cache,
2625
)
@@ -113,23 +112,6 @@ def test_get_dspark_swa_index_width(
113112
assert get_dspark_swa_index_width(window_size, num_speculative_tokens) == expected
114113

115114

116-
def test_compute_global_topk_reuses_output_buffers():
117-
device = "cuda"
118-
topk_indices = torch.tensor(
119-
[[0, 3, -1], [1, 2, -1]], dtype=torch.int32, device=device
120-
)
121-
token_to_req = torch.tensor([0, 1], dtype=torch.int32, device=device)
122-
block_table = torch.tensor([[5, 7], [11, 13]], dtype=torch.int32, device=device)
123-
is_valid = torch.tensor([True, False], device=device)
124-
args = (topk_indices, token_to_req, block_table, 2, is_valid)
125-
expected = compute_global_topk_indices_and_lens(*args)
126-
outputs = tuple(torch.empty_like(tensor) for tensor in expected)
127-
actual = compute_global_topk_indices_and_lens(*args, output_buffers=outputs)
128-
for result, output, reference in zip(actual, outputs, expected):
129-
assert result.data_ptr() == output.data_ptr()
130-
torch.testing.assert_close(result, reference)
131-
132-
133115
def _ue8m0_reference(x: torch.Tensor, block_size: int, fp8_max: float):
134116
"""PyTorch reference for UE8M0 FP8 quantization (per-block, power-of-2 scale).
135117

tests/kernels/test_fused_deepseek_v4_qnorm_rope_kv_insert.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -257,18 +257,8 @@ def test_q_path_matches_reference(num_tokens: int, n_heads: int, padded_heads: i
257257
num_blocks, bs, HEAD_BYTES, dtype=torch.uint8, device=device
258258
).view(num_blocks, -1)
259259
slot_mapping = torch.full((num_tokens,), -1, dtype=torch.int64, device=device)
260-
q_out = torch.empty(num_tokens, padded_heads, HEAD_DIM, dtype=dtype, device=device)
261-
torch.ops._C.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
262-
q,
263-
kv,
264-
q_out,
265-
k_cache,
266-
slot_mapping,
267-
positions,
268-
cos_sin_cache,
269-
padded_heads,
270-
eps,
271-
bs,
260+
q_out = _call_fused(
261+
q, padded_heads, kv, k_cache, slot_mapping, positions, cos_sin_cache, eps, bs
272262
)
273263

274264
torch.testing.assert_close(q_out[:, :n_heads], q_ref, rtol=1e-2, atol=1e-2)

tests/kernels/test_fused_indexer_q_rope_quant.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -150,23 +150,6 @@ def test_fused_indexer_q_rope_quant_matches_unfused(
150150
q_quant_ref, weights_ref = _reference(
151151
positions, q, cos_sin_cache, weights, softmax_scale, head_scale, use_fp4
152152
)
153-
output_buffers: tuple[torch.Tensor, ...] | None = None
154-
OUTPUT_BUFFER_TEST_NUM_TOKENS = 7
155-
if num_tokens == OUTPUT_BUFFER_TEST_NUM_TOKENS and cache_dtype == torch.float32:
156-
if use_fp4:
157-
q_ref, q_scale_ref = q_quant_ref
158-
output_buffers = (
159-
torch.empty_like(q_ref),
160-
torch.empty_like(q_scale_ref)
161-
.view(torch.uint8)
162-
.reshape(num_tokens, N_HEAD, -1),
163-
torch.empty_like(weights_ref),
164-
)
165-
else:
166-
output_buffers = (
167-
torch.empty_like(q_quant_ref),
168-
torch.empty_like(weights_ref),
169-
)
170153
# use_cutedsl=False: force the triton path even when cutedsl is installed
171154
# by patching the dispatcher's has_cutedsl() binding to return False.
172155
cutedsl_patch = (
@@ -186,17 +169,8 @@ def test_fused_indexer_q_rope_quant_matches_unfused(
186169
softmax_scale,
187170
head_scale,
188171
use_fp4,
189-
output_buffers=output_buffers,
190172
)
191173

192-
if output_buffers is not None:
193-
if use_fp4:
194-
assert q_quant_fused[0].data_ptr() == output_buffers[0].data_ptr()
195-
assert q_quant_fused[1].data_ptr() == output_buffers[1].data_ptr()
196-
else:
197-
assert q_quant_fused.data_ptr() == output_buffers[0].data_ptr()
198-
assert weights_fused.data_ptr() == output_buffers[-1].data_ptr()
199-
200174
if use_fp4:
201175
q_quant_ref, q_scale_ref = q_quant_ref
202176
q_quant_fused, q_scale_fused = q_quant_fused

vllm/models/deepseek_v4/attention.py

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
from vllm.models.deepseek_v4.common.ops.fused_indexer_q import MXFP4_BLOCK_SIZE
3030

3131
if TYPE_CHECKING:
32-
from vllm.models.deepseek_v4.eager_scratch import DeepseekV4EagerScratchPool
3332
from vllm.v1.attention.backends.mla.sparse_swa import (
3433
DeepseekSparseSWAMetadata,
3534
)
@@ -185,7 +184,6 @@ def __init__(
185184
prefix: str,
186185
topk_indices_buffer: torch.Tensor | None = None,
187186
aux_stream_list: list[torch.cuda.Stream] | None = None,
188-
eager_scratch_pool: "DeepseekV4EagerScratchPool | None" = None,
189187
) -> None:
190188
super().__init__()
191189
config = vllm_config.model_config.hf_config
@@ -274,7 +272,6 @@ def __init__(
274272
)
275273
self.indexer_rotary_emb = self.rotary_emb
276274
self.topk_indices_buffer = topk_indices_buffer
277-
self.eager_scratch_pool = eager_scratch_pool
278275

279276
self.indexer = None
280277
if self.compress_ratio == 4:
@@ -296,7 +293,6 @@ def __init__(
296293
compress_ratio=self.compress_ratio,
297294
prefix=f"{prefix}.indexer",
298295
aux_stream=indexer_aux_stream,
299-
eager_scratch_pool=eager_scratch_pool,
300296
)
301297

302298
self._prepare_and_attn_fn = self._prepare_and_attn
@@ -355,7 +351,6 @@ def __init__(
355351
rotate=True,
356352
prefix=f"{prefix}.compressor",
357353
k_cache_prefix=self.prefix,
358-
eager_scratch_pool=eager_scratch_pool,
359354
)
360355

361356
def forward(
@@ -637,24 +632,10 @@ def _fused_qnorm_rope_kv_insert(
637632
if cache_dtype == torch.uint8:
638633
# fp8_ds_mla UE8M0 paged path. Horizontally fused:
639634
# Q side: per-head RMSNorm (no weight) + GPT-J RoPE, zero-filling
640-
# the padding head slots.
635+
# the padding head slots; the kernel allocates and returns
636+
# the padded q tensor.
641637
# KV side: GPT-J RoPE + UE8M0 FP8 quant + paged cache insert.
642638
swa_kv_cache_2d = swa_kv_cache.view(swa_kv_cache.shape[0], -1)
643-
if self.eager_scratch_pool is not None:
644-
q_out = self.eager_scratch_pool.q_out(q.shape[0])
645-
torch.ops._C.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert_out(
646-
q,
647-
kv,
648-
q_out,
649-
swa_kv_cache_2d,
650-
swa_metadata.slot_mapping,
651-
positions,
652-
cos_sin_cache,
653-
self.padded_heads,
654-
self.eps,
655-
swa_metadata.block_size,
656-
)
657-
return q_out
658639
return torch.ops._C.fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert(
659640
q,
660641
kv,
@@ -703,13 +684,6 @@ def _fused_qnorm_rope_kv_insert(
703684
)
704685
return q_fp8
705686

706-
def _global_topk_output_buffers(
707-
self, topk_indices: torch.Tensor
708-
) -> tuple[torch.Tensor, torch.Tensor] | None:
709-
if self.compress_ratio != 4 or self.eager_scratch_pool is None:
710-
return None
711-
return self.eager_scratch_pool.global_topk_outputs(topk_indices)
712-
713687
def get_attn_backend(self) -> type[AttentionBackend]:
714688
return self.backend_cls
715689

@@ -792,7 +766,6 @@ def __init__(
792766
compress_ratio: int = 1,
793767
prefix: str = "",
794768
aux_stream: torch.cuda.Stream | None = None,
795-
eager_scratch_pool: "DeepseekV4EagerScratchPool | None" = None,
796769
):
797770
super().__init__()
798771
self.vllm_config = vllm_config
@@ -805,7 +778,6 @@ def __init__(
805778
self.rope_dim = config.qk_rope_head_dim # 64
806779
self.q_lora_rank = q_lora_rank # 1536
807780
self.compress_ratio = compress_ratio
808-
self.eager_scratch_pool = eager_scratch_pool
809781
self.use_fp4_kv = dsa_indexer_uses_fp4(vllm_config)
810782
logger.info_once(
811783
"Using %s indexer cache for Lightning Indexer.",
@@ -869,7 +841,6 @@ def __init__(
869841
prefix=f"{prefix}.compressor",
870842
k_cache_prefix=self.k_cache.prefix,
871843
use_fp4_cache=self.use_fp4_kv,
872-
eager_scratch_pool=eager_scratch_pool,
873844
)
874845

875846
self.indexer_op = SparseAttnIndexer(
@@ -933,9 +904,6 @@ def wq_b_and_q_quant():
933904
# ReplicatedLinear returns (output, bias); bias is None.
934905
q, _ = self.wq_b(qr)
935906
q = q.view(-1, self.n_head, self.head_dim)
936-
outputs = None
937-
if self.eager_scratch_pool is not None and self.use_fp4_kv:
938-
outputs = self.eager_scratch_pool.indexer_q_outputs(q.shape[0])
939907
return fused_indexer_q_rope_quant(
940908
positions,
941909
q,
@@ -944,7 +912,6 @@ def wq_b_and_q_quant():
944912
self.softmax_scale,
945913
self.n_head**-0.5,
946914
use_fp4=self.use_fp4_kv,
947-
output_buffers=outputs,
948915
)
949916

950917
# compressor returns None and writes K to the indexer KV cache; the

vllm/models/deepseek_v4/common/ops/cache_utils.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,6 @@ def compute_global_topk_indices_and_lens(
438438
block_table: torch.Tensor,
439439
block_size: int,
440440
is_valid_token: torch.Tensor,
441-
output_buffers: tuple[torch.Tensor, torch.Tensor] | None = None,
442441
) -> tuple[torch.Tensor, torch.Tensor]:
443442
"""Map local topk indices to global KV cache slots and count valid entries.
444443
@@ -448,15 +447,8 @@ def compute_global_topk_indices_and_lens(
448447
3. Masking padding tokens to length 0
449448
"""
450449
num_tokens = topk_indices.shape[0]
451-
if output_buffers is None:
452-
global_topk_indices = torch.empty_like(topk_indices)
453-
topk_lens = torch.empty(
454-
num_tokens, dtype=torch.int32, device=topk_indices.device
455-
)
456-
else:
457-
global_topk_indices, topk_lens = output_buffers
458-
assert global_topk_indices.shape == topk_indices.shape
459-
assert topk_lens.shape == (num_tokens,)
450+
global_topk_indices = torch.empty_like(topk_indices)
451+
topk_lens = torch.empty(num_tokens, dtype=torch.int32, device=topk_indices.device)
460452
_compute_global_topk_indices_and_lens_kernel[(num_tokens,)](
461453
global_topk_indices,
462454
global_topk_indices.stride(0),

0 commit comments

Comments
 (0)