18-v1_kv_cache_interface.py.diff 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. --- baked-ours/v1/kv_cache_interface.py 2026-09-10 19:56:14.000000000 +0200
  2. +++ baked/v1/kv_cache_interface.py 2026-09-11 10:13:47.000000000 +0200
  3. @@ -240,8 +240,11 @@
  4. f"Unsupported KV cache spec type: {type(self)}. "
  5. "Please register it using @register_kv_cache_spec decorator."
  6. )
  7. + dcp_replicated = getattr(self, "dcp_replicated", None)
  8. return all(
  9. - isinstance(spec, uniform_type_base_spec) for spec in kv_cache_specs.values()
  10. + isinstance(spec, uniform_type_base_spec)
  11. + and getattr(spec, "dcp_replicated", None) == dcp_replicated
  12. + for spec in kv_cache_specs.values()
  13. )
  14. @@ -400,6 +403,13 @@
  15. """Tokens covered by one stored state. Ints > 1 compress multiple tokens
  16. into one state (DSv4 sparse MLA); fractions < 1 store multiple states per
  17. token (Whisper block pooling: ``Fraction(1, block_pool_size)``)."""
  18. + dcp_replicated: bool = False
  19. + """DCP: every rank holds this layer's full KV instead of a 1/dcp shard.
  20. + A replicated group keeps its plain block geometry under DCP (no block-size
  21. + scaling, no per-rank memory divide, block table sized for the whole
  22. + context) and every rank writes every position (DeepSeek-V4.1 SWA cache and
  23. + compressor ring). Sharded groups (the default) round-robin positions across
  24. + ranks at ``cp_kv_cache_interleave_size`` granularity."""
  25. def __post_init__(self):
  26. if self.head_size_v is None:
  27. @@ -438,7 +448,9 @@
  28. def max_num_blocks_per_req(self, vllm_config: VllmConfig, max_len: int) -> int:
  29. parallel_config = vllm_config.parallel_config
  30. - kv_shard_count = parallel_config.decode_context_parallel_size
  31. + kv_shard_count = (
  32. + 1 if self.dcp_replicated else parallel_config.decode_context_parallel_size
  33. + )
  34. return cdiv(max_len, self.block_size * kv_shard_count)
  35. @@ -471,7 +483,7 @@
  36. def max_memory_usage_bytes(self, vllm_config: VllmConfig) -> int:
  37. max_model_len = vllm_config.model_config.max_model_len
  38. dcp_world_size = vllm_config.parallel_config.decode_context_parallel_size
  39. - if dcp_world_size > 1:
  40. + if dcp_world_size > 1 and not self.dcp_replicated:
  41. max_model_len = cdiv(max_model_len, dcp_world_size)
  42. return cdiv(max_model_len, self.block_size) * self.page_size_bytes
  43. @@ -519,6 +531,7 @@
  44. num_head_slots=specs[0].num_head_slots,
  45. state_content_bytes=specs[0].state_content_bytes,
  46. tokens_per_state=specs[0].tokens_per_state,
  47. + dcp_replicated=specs[0].dcp_replicated,
  48. sliding_window=cls.merge_window_sizes(sliding_window),
  49. attention_chunk_size=cls.merge_window_sizes(attention_chunk_size),
  50. # If any layer in the group is non-causal, treat the group as
  51. @@ -602,6 +615,7 @@
  52. state_content_bytes=specs[0].state_content_bytes,
  53. cache_dtype_str=cache_dtype_str_set.pop(),
  54. tokens_per_state=tokens_per_state_set.pop(),
  55. + dcp_replicated=specs[0].dcp_replicated,
  56. model_version=model_version_set.pop(),
  57. storage_block_size=storage_block_size_set.pop(),
  58. non_causal_multi_token_decode=non_causal_mtd_set.pop(),
  59. @@ -657,6 +671,7 @@
  60. num_head_slots=base.num_head_slots,
  61. state_content_bytes=base.state_content_bytes,
  62. tokens_per_state=base.tokens_per_state,
  63. + dcp_replicated=base.dcp_replicated,
  64. sliding_window=base.sliding_window,
  65. attention_chunk_size=base.attention_chunk_size,
  66. non_causal=base.non_causal,
  67. @@ -700,6 +715,7 @@
  68. return all(
  69. isinstance(spec, ChunkedLocalAttentionSpec)
  70. and spec.attention_chunk_size == self.attention_chunk_size
  71. + and spec.dcp_replicated == self.dcp_replicated
  72. for spec in kv_cache_specs.values()
  73. )
  74. @@ -743,9 +759,10 @@
  75. return cdiv(num_tokens, self.block_size) + 1
  76. def max_memory_usage_bytes(self, vllm_config: VllmConfig) -> int:
  77. - assert vllm_config.parallel_config.decode_context_parallel_size == 1, (
  78. - "DCP not support sliding window."
  79. - )
  80. + assert (
  81. + vllm_config.parallel_config.decode_context_parallel_size == 1
  82. + or self.dcp_replicated
  83. + ), "DCP not support sliding window (sharded); mark the spec dcp_replicated."
  84. max_blocks = self.max_admission_blocks_per_request(
  85. max_in_flight_tokens=vllm_config.max_in_flight_tokens,
  86. max_model_len=vllm_config.model_config.max_model_len,
  87. @@ -758,6 +775,7 @@
  88. return all(
  89. isinstance(spec, SlidingWindowSpec)
  90. and spec.sliding_window == self.sliding_window
  91. + and spec.dcp_replicated == self.dcp_replicated
  92. for spec in kv_cache_specs.values()
  93. )
  94. @@ -786,7 +804,9 @@
  95. self, kv_cache_specs: dict[str, KVCacheSpec]
  96. ) -> bool:
  97. return all(
  98. - isinstance(spec, CircularBufferSpec) for spec in kv_cache_specs.values()
  99. + isinstance(spec, CircularBufferSpec)
  100. + and spec.dcp_replicated == self.dcp_replicated
  101. + for spec in kv_cache_specs.values()
  102. )
  103. @property
  104. @@ -847,6 +867,7 @@
  105. extra_retained_tokens=extra_retained_set.pop(),
  106. cache_dtype_str=cache_dtype_str_set.pop(),
  107. tokens_per_state=tokens_per_state_set.pop(),
  108. + dcp_replicated=specs[0].dcp_replicated,
  109. model_version=model_version_set.pop(),
  110. )
  111. @@ -856,6 +877,7 @@
  112. return all(
  113. isinstance(spec, SlidingWindowMLASpec)
  114. and spec.sliding_window == self.sliding_window
  115. + and spec.dcp_replicated == self.dcp_replicated
  116. for spec in kv_cache_specs.values()
  117. )
  118. @@ -1013,6 +1035,7 @@
  119. page_size_padded=specs[0].page_size_padded,
  120. num_head_slots=specs[0].num_head_slots,
  121. state_content_bytes=specs[0].state_content_bytes,
  122. + dcp_replicated=specs[0].dcp_replicated,
  123. sliding_window=cls.merge_window_sizes(sliding_window),
  124. attention_chunk_size=cls.merge_window_sizes(attention_chunk_size),
  125. non_causal=any(spec.non_causal for spec in specs),
  126. @@ -1147,6 +1170,28 @@
  127. )
  128. +def is_dcp_replicated_spec(kv_cache_spec: KVCacheSpec) -> bool:
  129. + """Whether a KV cache group spec is (or wraps) DCP-replicated attention
  130. + layers: every DCP rank holds the full KV, so the group keeps its plain
  131. + block geometry and every rank writes every position."""
  132. + layer_specs = iter_layer_specs(kv_cache_spec)
  133. + return len(layer_specs) > 0 and all(
  134. + isinstance(spec, AttentionSpec) and spec.dcp_replicated
  135. + for spec in layer_specs
  136. + )
  137. +
  138. +
  139. +def is_dcp_sharded_spec(kv_cache_spec: KVCacheSpec) -> bool:
  140. + """Whether a KV cache group spec is (or wraps) DCP-sharded full attention
  141. + (incl. MLA): positions are round-robined across DCP ranks, so manager block
  142. + size, prefix hashing and per-rank memory scale by the DCP size."""
  143. + layer_specs = iter_layer_specs(kv_cache_spec)
  144. + return len(layer_specs) > 0 and all(
  145. + isinstance(spec, FullAttentionSpec) and not spec.dcp_replicated
  146. + for spec in layer_specs
  147. + )
  148. +
  149. +
  150. def get_kv_cache_spec_kind(kv_cache_spec: KVCacheSpec) -> KVCacheSpecKind:
  151. if isinstance(kv_cache_spec, UniformTypeKVCacheSpecs):
  152. inner_kinds = {