@@ -236,15 +236,11 @@ static llama_kv_cache_dsv4_context::comp_plan dsv4_build_comp_plan(
236236 std::vector<int32_t > overlap_prev_reads;
237237 std::vector<int32_t > overlap_cur_reads;
238238
239- const auto current_token_idx = [&](llama_seq_id seq_id, llama_pos pos) -> int64_t {
240- for (uint32_t i = 0 ; i < ubatch.n_tokens ; ++i) {
241- if (ubatch.pos [i] == pos && ubatch.seq_id [i][0 ] == seq_id) {
242- return i;
243- }
244- }
239+ std::map<std::pair<llama_seq_id, llama_pos>, int64_t > curr_token_idx_map;
245240
246- return -1 ;
247- };
241+ for (uint32_t i = 0 ; i < ubatch.n_tokens ; ++i) {
242+ curr_token_idx_map.emplace (std::make_pair (ubatch.seq_id [i][0 ], ubatch.pos [i]), i);
243+ }
248244
249245 const auto state_source_idx = [&](llama_seq_id seq_id, llama_pos pos) -> int32_t {
250246 if (pos < 0 ) {
@@ -254,9 +250,9 @@ static llama_kv_cache_dsv4_context::comp_plan dsv4_build_comp_plan(
254250 return (int32_t ) (state_rows + ubatch.n_tokens );
255251 }
256252
257- const int64_t tok_idx = current_token_idx (seq_id, pos);
258- if (tok_idx >= 0 ) {
259- return (int32_t ) (state_rows + tok_idx );
253+ const auto key = std::make_pair (seq_id, pos);
254+ if (curr_token_idx_map. find (key) != curr_token_idx_map. end () ) {
255+ return (int32_t ) (state_rows + curr_token_idx_map. at (key) );
260256 }
261257
262258 const int64_t stream_off = n_stream > 1 ? (int64_t ) seq_id*state_size : 0 ;
@@ -321,6 +317,8 @@ static llama_kv_cache_dsv4_context::comp_plan dsv4_build_comp_plan(
321317 }
322318
323319 if (ratio == DSV4_CSA_RATIO && plan.state_write_idxs .empty () && !plan.state_pos .empty ()) {
320+ // Non-boundary CSA steps still need a write op so their graph matches
321+ // boundary steps. Use a padded scratch row that is masked from attention.
324322 assert (kv_size > 0 );
325323
326324 uint32_t i = 0 ;
0 commit comments