[Disagg][Qwen3.5] Fix heterogeneous attn-TP scatter transfer: GDN conv sub-block slice + GQA replicated-KV head map (#30997)
Co-authored-by: Xuwei Li <lixuwei.xy@gmail.com>
This commit is contained in:
@@ -677,6 +677,9 @@ class MambaPool:
|
||||
)
|
||||
self.mem_usage = mem_usage_bytes / GB
|
||||
self.num_mamba_layers = num_mamba_layers
|
||||
# Full (unsharded) conv sub-block dims for PD transfer across different
|
||||
# attn_tp_size (GDN: [key_dim, key_dim, value_dim]); None otherwise.
|
||||
self.conv_shard_groups = getattr(cache_params.shape, "conv_shard_groups", None)
|
||||
|
||||
def get_speculative_mamba2_params_all_layers(self) -> SpeculativeState:
|
||||
assert isinstance(self.mamba_cache, self.SpeculativeState)
|
||||
@@ -832,6 +835,43 @@ class MambaPool:
|
||||
dim_per_tensor += [sliceable_dim] * self.num_mamba_layers
|
||||
return dim_per_tensor
|
||||
|
||||
def get_state_conv_shard_groups(self):
|
||||
"""Per-tensor conv sub-block dims, aligned element-wise with
|
||||
get_state_dim_per_tensor().
|
||||
|
||||
For GDN, conv_state's sliceable axis is cat([query, key, value]) with
|
||||
each sub-block head-sharded independently across attn-TP; the full
|
||||
(unsharded) sub-block dims are returned so PD transfer across different
|
||||
attn_tp_size can slice each sub-block. Returns None for temporal_state
|
||||
(single head-sharded axis) and whenever no descriptor is available, so
|
||||
those tensors keep the single contiguous slice.
|
||||
"""
|
||||
subdims_per_tensor = []
|
||||
for field in vars(self.mamba_cache):
|
||||
# Mirror the exclusions in get_state_dim_per_tensor so the returned
|
||||
# sub-dims line up element-wise with the RDMA buffer list.
|
||||
if field in (
|
||||
"intermediate_ssm",
|
||||
"intermediate_conv_window",
|
||||
"replayssm_d",
|
||||
"replayssm_k",
|
||||
"replayssm_g",
|
||||
):
|
||||
continue
|
||||
value = getattr(self.mamba_cache, field)
|
||||
if value is None:
|
||||
continue
|
||||
tensors = value if isinstance(value, list) else [value]
|
||||
for _ in tensors:
|
||||
# Only conv_state carries a q/k/v decomposition.
|
||||
subdims = (
|
||||
list(self.conv_shard_groups)
|
||||
if field == "conv" and self.conv_shard_groups is not None
|
||||
else None
|
||||
)
|
||||
subdims_per_tensor += [subdims] * self.num_mamba_layers
|
||||
return subdims_per_tensor
|
||||
|
||||
|
||||
class HybridReqToTokenPool(ReqToTokenPool):
|
||||
"""A memory pool that maps a request to its token locations."""
|
||||
@@ -1022,6 +1062,9 @@ class HybridReqToTokenPool(ReqToTokenPool):
|
||||
def get_state_dim_per_tensor(self):
|
||||
return self.mamba_pool.get_state_dim_per_tensor()
|
||||
|
||||
def get_state_conv_shard_groups(self):
|
||||
return self.mamba_pool.get_state_conv_shard_groups()
|
||||
|
||||
def get_mamba_ping_pong_other_idx(self, mamba_next_track_idx: int) -> int:
|
||||
if self.mamba_ping_pong_track_buffer_size == 2:
|
||||
return 1 - mamba_next_track_idx
|
||||
@@ -2598,6 +2641,10 @@ class HybridLinearKVPool(KVCache):
|
||||
"""Get the sliceable dimension size for each mamba state tensor."""
|
||||
return self.mamba_pool.get_state_dim_per_tensor()
|
||||
|
||||
def get_state_conv_shard_groups(self):
|
||||
"""Per-tensor conv sub-block dims (GDN) aligned with the state list."""
|
||||
return self.mamba_pool.get_state_conv_shard_groups()
|
||||
|
||||
def maybe_get_custom_mem_pool(self):
|
||||
return self.full_kv_pool.maybe_get_custom_mem_pool()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user