[NPU] Support DeepSeek-V4 DSpark and refactor DSV4 cache management (#33676)
Co-authored-by: JiaruiChang5268 <jc5268@columbia.edu> Co-authored-by: Kelon <kelonlu@163.com> Co-authored-by: unknown <z8ruev42yk@gmail.com> Co-authored-by: Talantan1102 <545811257@qq.com> Co-authored-by: Talantan1102 <44429302+Talantan1102@users.noreply.github.com>
This commit is contained in:
co-authored by
JiaruiChang5268
Kelon
unknown
Talantan1102
Talantan1102
parent
e03c53fc13
commit
b83d507cd7
@@ -59,7 +59,6 @@ from sglang.srt.hardware_backend.npu.attention.ascend_dsv4_backend import (
|
||||
DeepseekV4AscendMultiStepDraftBackend,
|
||||
_apply_hadamard,
|
||||
_get_kv_indices,
|
||||
_overlap_transform,
|
||||
_walsh_hadamard_matrix,
|
||||
)
|
||||
|
||||
@@ -182,78 +181,6 @@ class TestApplyHadamard(unittest.TestCase):
|
||||
self.assertTrue(torch.equal(out, expected))
|
||||
|
||||
|
||||
class TestOverlapTransform(unittest.TestCase):
|
||||
def test_shape(self):
|
||||
# (n_chunks, ratio, 2*d) -> (n_chunks, 2*ratio, d)
|
||||
n_chunks, r, d = 3, 2, 4
|
||||
tensor = torch.randn(n_chunks, r, 2 * d)
|
||||
out = _overlap_transform(tensor, value=0.0, head_dim=d)
|
||||
self.assertEqual(out.shape, (n_chunks, 2 * r, d))
|
||||
|
||||
def test_first_chunk_left_half_filled_with_value(self):
|
||||
n_chunks, r, d = 3, 2, 4
|
||||
tensor = torch.randn(n_chunks, r, 2 * d)
|
||||
fill = float("-inf")
|
||||
out = _overlap_transform(tensor, value=fill, head_dim=d)
|
||||
self.assertTrue(torch.equal(out[0, :r], torch.full((r, d), fill)))
|
||||
|
||||
def test_first_chunk_left_half_filled_with_zero(self):
|
||||
n_chunks, r, d = 2, 2, 4
|
||||
tensor = torch.randn(n_chunks, r, 2 * d)
|
||||
out = _overlap_transform(tensor, value=0.0, head_dim=d)
|
||||
self.assertTrue(torch.equal(out[0, :r], torch.zeros(r, d)))
|
||||
|
||||
def test_right_half_mirrors_tensor_second_half(self):
|
||||
n_chunks, r, d = 3, 2, 4
|
||||
tensor = torch.randn(n_chunks, r, 2 * d)
|
||||
out = _overlap_transform(tensor, value=0.0, head_dim=d)
|
||||
self.assertTrue(torch.equal(out[:, r:], tensor[..., d:]))
|
||||
|
||||
def test_previous_chunk_left_half(self):
|
||||
n_chunks, r, d = 3, 2, 4
|
||||
tensor = torch.randn(n_chunks, r, 2 * d)
|
||||
out = _overlap_transform(tensor, value=0.0, head_dim=d)
|
||||
self.assertTrue(torch.equal(out[1:, :r], tensor[:-1, :, :d]))
|
||||
|
||||
def test_single_chunk(self):
|
||||
n_chunks, r, d = 1, 2, 4
|
||||
tensor = torch.randn(n_chunks, r, 2 * d)
|
||||
fill = 7.0
|
||||
out = _overlap_transform(tensor, value=fill, head_dim=d)
|
||||
self.assertEqual(out.shape, (1, 2 * r, d))
|
||||
self.assertTrue(torch.equal(out[0, :r], torch.full((r, d), fill)))
|
||||
self.assertTrue(torch.equal(out[0, r:], tensor[0, :, d:]))
|
||||
|
||||
def test_full_element_mapping(self):
|
||||
n_chunks, r, d = 2, 2, 3
|
||||
tensor = torch.arange(n_chunks * r * 2 * d, dtype=torch.float32).reshape(
|
||||
n_chunks, r, 2 * d
|
||||
)
|
||||
fill = -1.0
|
||||
out = _overlap_transform(tensor, value=fill, head_dim=d)
|
||||
|
||||
for c in range(n_chunks):
|
||||
for row in range(2 * r):
|
||||
for col in range(d):
|
||||
if c == 0 and row < r:
|
||||
expected = fill
|
||||
elif row >= r:
|
||||
expected = tensor[c, row - r, d + col].item()
|
||||
else:
|
||||
expected = tensor[c - 1, row, col].item()
|
||||
self.assertEqual(
|
||||
out[c, row, col].item(),
|
||||
expected,
|
||||
f"mismatch at (c={c}, row={row}, col={col})",
|
||||
)
|
||||
|
||||
def test_preserves_input_dtype(self):
|
||||
n_chunks, r, d = 2, 2, 4
|
||||
tensor = torch.randn(n_chunks, r, 2 * d, dtype=torch.bfloat16)
|
||||
out = _overlap_transform(tensor, value=0.0, head_dim=d)
|
||||
self.assertEqual(out.dtype, torch.bfloat16)
|
||||
|
||||
|
||||
class TestGetKvIndices(unittest.TestCase):
|
||||
_PATCH_TARGET = (
|
||||
"sglang.srt.hardware_backend.npu.attention.ascend_dsv4_backend.get_attn_backend"
|
||||
|
||||
@@ -41,6 +41,10 @@ _EVICT_METHOD = "maybe_evict_swa"
|
||||
# Any added/removed/recounted site fails until reviewed here.
|
||||
_SB = "managers/schedule_batch.py"
|
||||
_EAGLE_DECODE = ("speculative/eagle_utils.py", "eagle_prepare_for_decode")
|
||||
_DFLASH_DECODE = (
|
||||
"speculative/dflash_info_v2.py",
|
||||
"DFlashDraftInputV2.prepare_for_decode",
|
||||
)
|
||||
_RESOLVE = (
|
||||
"managers/scheduler_components/batch_result_processor.py",
|
||||
"SchedulerBatchResultProcessor._resolve_spec_v2_tokens",
|
||||
@@ -62,6 +66,11 @@ _OWNER_SITES = {
|
||||
# inside the owned-kv alloc_for_spec_decode function (op42).
|
||||
(*_EAGLE_DECODE, "decode_batch_idx"): 1,
|
||||
(*_EAGLE_DECODE, "evict"): 1,
|
||||
# DFlash uses its stateful scheduler-side preparation instead of
|
||||
# eagle_prepare_for_decode. spec_prepare_for_decode dispatches to exactly
|
||||
# one of these two owners for each speculative decode iteration.
|
||||
(*_DFLASH_DECODE, "decode_batch_idx"): 1,
|
||||
(*_DFLASH_DECODE, "evict"): 1,
|
||||
(
|
||||
"mem_cache/allocation.py",
|
||||
"alloc_for_spec_decode",
|
||||
|
||||
Reference in New Issue
Block a user