@@ -484,6 +484,7 @@ def __init__(
484484
485485 # dsa c8
486486 self .use_sparse_c8_indexer = ascend_config .is_sparse_c8_layer (self .layer_name )
487+ self .use_a5_sparse_c8_indexer = self .use_sparse_c8_indexer and (get_ascend_device_type () == AscendDeviceType .A5 )
487488 if self .use_sparse_c8_indexer :
488489 if get_ascend_device_type () == AscendDeviceType .A5 :
489490 self .c8_k_cache_dtype = torch .float8_e4m3fn
@@ -961,6 +962,21 @@ def exec_kv(
961962 cache_mode = "PA"
962963
963964 if self .enable_dsa_cp :
965+ if self .use_a5_sparse_c8_indexer :
966+ k_pe , k_nope , knope_scale = custom_kv_rmsnorm_rope (
967+ kv_no_split ,
968+ self .kv_a_layernorm .weight , # type: ignore[union-attr]
969+ cos ,
970+ sin ,
971+ slots .to (torch .int64 ),
972+ kv_cache [1 ],
973+ kv_cache [0 ],
974+ epsilon = self .kv_a_layernorm .variance_epsilon , # type: ignore[union-attr]
975+ cache_mode = cache_mode ,
976+ is_output_kv = True ,
977+ )
978+ return k_pe , k_nope , knope_scale
979+
964980 _ , _ , k_pe , k_nope = torch_npu .npu_kv_rmsnorm_rope_cache (
965981 kv_no_split ,
966982 self .kv_a_layernorm .weight , # type: ignore[union-attr]
@@ -973,7 +989,8 @@ def exec_kv(
973989 cache_mode = cache_mode ,
974990 is_output_kv = True ,
975991 )
976- return k_pe , k_nope
992+ knope_scale = None
993+ return k_pe , k_nope , knope_scale
977994 else :
978995 torch_npu .npu_kv_rmsnorm_rope_cache (
979996 kv_no_split ,
@@ -1283,7 +1300,9 @@ def forward(
12831300
12841301 if self .enable_dsa_cp :
12851302 assert slot_mapping_cp is not None
1286- k_pe , k_nope = self .exec_kv (kv_no_split , cos , sin , kv_cache , slot_mapping_cp , attn_metadata )
1303+ k_pe , k_nope , knope_scale = self .exec_kv (
1304+ kv_no_split , cos , sin , kv_cache , slot_mapping_cp , attn_metadata
1305+ )
12871306 else :
12881307 k_pe , k_nope = self .exec_kv (kv_no_split , cos , sin , kv_cache , slot_mapping , attn_metadata )
12891308
@@ -1306,6 +1325,32 @@ def forward(
13061325 get_tp_group (),
13071326 async_op = async_op ,
13081327 )
1328+ elif self .use_a5_sparse_c8_indexer :
1329+ # due to different dtypes, we have to split commu pass
1330+ assert knope_scale is not None
1331+ assert k_li_scale is not None
1332+ fused_kv_no_split , _ = all_gather_async (
1333+ torch .cat (
1334+ [
1335+ k_nope .view (- 1 , k_nope .shape [- 1 ]),
1336+ k_pe .view (- 1 , k_pe .shape [- 1 ]),
1337+ knope_scale .view (- 1 , knope_scale .shape [- 1 ]),
1338+ ],
1339+ dim = 1 ,
1340+ ),
1341+ get_tp_group (),
1342+ async_op = async_op ,
1343+ )
1344+ k_li , _ = all_gather_async (
1345+ k_li ,
1346+ get_tp_group (),
1347+ async_op = async_op ,
1348+ )
1349+ k_li_scale , kv_ag_handle = all_gather_async (
1350+ k_li_scale ,
1351+ get_tp_group (),
1352+ async_op = async_op ,
1353+ )
13091354 else :
13101355 # due to different dtypes, we have to split commu pass
13111356 assert k_li_scale is not None
@@ -1359,17 +1404,26 @@ def forward(
13591404 k_pe , k_nope , k_li = fused_kv_no_split .split (
13601405 [self .qk_rope_head_dim , self .kv_lora_rank , self .head_dim ], dim = - 1
13611406 )
1407+ elif self .use_a5_sparse_c8_indexer :
1408+ torch_npu .npu_scatter_nd_update_ (
1409+ kv_cache [0 ].view (- 1 , fused_kv_no_split .shape [- 1 ]),
1410+ slot_mapping [: attn_metadata .num_actual_tokens ].view (- 1 , 1 ),
1411+ fused_kv_no_split [: attn_metadata .num_actual_tokens ],
1412+ )
1413+ k_pe = None
1414+ k_nope = None
13621415 else :
13631416 k_pe , k_nope = fused_kv_no_split .split ([self .qk_rope_head_dim , self .kv_lora_rank ], dim = - 1 )
1364- k_nope = k_nope .view (k_nope .shape [0 ], 1 , - 1 )
1365- k_pe = k_pe .view (k_pe .shape [0 ], 1 , - 1 )
1366- DeviceOperator .reshape_and_cache (
1367- key = k_nope [: attn_metadata .num_actual_tokens ],
1368- value = k_pe [: attn_metadata .num_actual_tokens ],
1369- key_cache = kv_cache [0 ],
1370- value_cache = kv_cache [1 ],
1371- slot_mapping = slot_mapping [: attn_metadata .num_actual_tokens ],
1372- )
1417+ if not self .use_a5_sparse_c8_indexer :
1418+ k_nope = k_nope .view (k_nope .shape [0 ], 1 , - 1 )
1419+ k_pe = k_pe .view (k_pe .shape [0 ], 1 , - 1 )
1420+ DeviceOperator .reshape_and_cache (
1421+ key = k_nope [: attn_metadata .num_actual_tokens ],
1422+ value = k_pe [: attn_metadata .num_actual_tokens ],
1423+ key_cache = kv_cache [0 ],
1424+ value_cache = kv_cache [1 ],
1425+ slot_mapping = slot_mapping [: attn_metadata .num_actual_tokens ],
1426+ )
13731427
13741428 k_li = self ._get_full_kv (k_li , attn_metadata )
13751429
@@ -1480,3 +1534,39 @@ def forward(
14801534 maybe_save_kv_layer_to_connector (layer_name , list (kv_cache ))
14811535
14821536 return output_padded
1537+
1538+
1539+ def custom_kv_rmsnorm_rope (
1540+ kv ,
1541+ gamma ,
1542+ cos ,
1543+ sin ,
1544+ index ,
1545+ k_cache ,
1546+ ckv_cache ,
1547+ k_rope_scale = None ,
1548+ c_kv_scale = None ,
1549+ k_rope_offset = None ,
1550+ c_kv_offset = None ,
1551+ v = None ,
1552+ epsilon = 1e-05 ,
1553+ cache_mode = "Norm" ,
1554+ is_output_kv = False ,
1555+ ):
1556+ # Split KV into RMSNorm and RoPE parts for sparse C8 cache preparation.
1557+ rms_in , rope_in = kv .split ([512 , 64 ], dim = - 1 )
1558+ k_nope , _ = torch_npu .npu_rms_norm (rms_in , gamma , epsilon = epsilon )
1559+ k_rope = torch_npu .npu_interleave_rope (rope_in , cos , sin )
1560+
1561+ # Store k_nope with block FP8 scales for the sparse C8 cache layout.
1562+ k_nope , knope_scale = torch_npu .npu_dynamic_block_quant (
1563+ k_nope .view (- 1 , 1 , k_nope .shape [- 1 ]),
1564+ dst_type = torch .float8_e4m3fn ,
1565+ row_block_size = 1 ,
1566+ col_block_size = 128 ,
1567+ )
1568+ return (
1569+ k_rope .view (torch .float8_e4m3fn ),
1570+ k_nope ,
1571+ knope_scale .view (knope_scale .shape [0 ], - 1 ).view (torch .float8_e4m3fn ),
1572+ )
0 commit comments