#include "llama-dsv4.h" #include #include "llama.h" #include "llama-context.h" #include "llama-model.h" #include "llama-impl.h" #include "ggml.h" #include "ggml-alloc.h" #include "ggml-backend.h" #include #include #include #include #include #include static bool dsv4_cache_type_supported(ggml_type type) { return type == GGML_TYPE_F16 || type == GGML_TYPE_BF16 || type == GGML_TYPE_Q8_0; } // Per-step capture is limited to the eight-row CSA/LID ring. // TODO: Expand to a larger number static constexpr int DSV4_PER_STEP_MAX_STATE_ROWS = 8; static bool dsv4_validate_cache_type(ggml_type type, int64_t width, const char * name) { if (!dsv4_cache_type_supported(type)) { LLAMA_LOG_ERROR("%s: unsupported DSV4 %s cache type %s\n", __func__, name, ggml_type_name(type)); return false; } if (ggml_is_quantized(type) && width % ggml_blck_size(type) != 0) { LLAMA_LOG_ERROR("%s: DSV4 %s cache width %d is not aligned to %d elements for %s\n", __func__, name, (int)width, (int)ggml_blck_size(type), ggml_type_name(type)); return false; } return true; } static ggml_backend_buffer_type_t llama_dsv4_layer_buft(const llama_context & lctx, int32_t il) { if (il >= 0 && il < (int32_t) lctx.model.buft_layer.size() && lctx.model.buft_layer[il].buft != nullptr) { return lctx.model.buft_layer[il].buft; } if (il >= 0 && il < (int32_t) lctx.model.layers.size()) { const ggml_tensor * ref = lctx.model.layers[il].attn_comp_wkv; if (ref == nullptr) { ref = lctx.model.layers[il].wq_a; } if (ref != nullptr && ref->buffer != nullptr) { return ggml_backend_buffer_get_type(ref->buffer); } } return llama_default_buffer_type_cpu(true); } static uint32_t dsv4_comp_size(uint32_t kv_size, uint32_t ratio) { return std::max(1, (kv_size + ratio - 1)/ratio); } static bool dsv4_validate_csa_lid_visibility( const llama_context & lctx, uint32_t csa_kv_size, uint32_t lid_kv_size) { const auto & csa_plan = lctx.dsv4.csa_plan; const auto & lid_plan = lctx.dsv4.lid_plan; const auto & csa_ctx = lctx.dsv4.csa_ctx; const auto & lid_ctx = lctx.dsv4.lid_ctx; if (csa_kv_size != lid_kv_size || csa_plan.n_stream != lid_plan.n_stream || csa_plan.n_kv != lid_plan.n_kv || csa_plan.n_visible != lid_plan.n_visible || csa_ctx.graph_n_stream != lid_ctx.graph_n_stream || csa_ctx.n_kv != lid_ctx.n_kv || csa_ctx.sinfo.strm != lid_ctx.sinfo.strm || csa_ctx.sinfo.idxs != lid_ctx.sinfo.idxs || csa_ctx.sinfo.s0 != lid_ctx.sinfo.s0 || csa_ctx.sinfo.s1 != lid_ctx.sinfo.s1) { LLAMA_LOG_ERROR("%s: DSV4 CSA/LID visibility contracts differ\n", __func__); return false; } return true; } static void dsv4_batch_shape( const llama_batch & batch, uint32_t & n_seqs, uint32_t & n_seq_tokens) { n_seqs = 1; n_seq_tokens = (uint32_t) std::max(1, batch.n_tokens); if (batch.n_tokens <= 0 || batch.n_seq_id == nullptr || batch.seq_id == nullptr) { return; } std::map counts; for (int32_t i = 0; i < batch.n_tokens; ++i) { if (batch.n_seq_id[i] != 1 || batch.seq_id[i] == nullptr) { return; } counts[batch.seq_id[i][0]]++; } if (counts.empty()) { return; } const uint32_t seq_tokens = counts.begin()->second; for (const auto & [_, count] : counts) { if (count != seq_tokens) { return; } } n_seqs = (uint32_t) counts.size(); n_seq_tokens = std::max(1, seq_tokens); } static bool dsv4_batch_has_coupled(const llama_batch & batch) { if (batch.n_tokens <= 0 || batch.n_seq_id == nullptr) { return false; } for (int32_t i = 0; i < batch.n_tokens; ++i) { if (batch.n_seq_id[i] > 1) { return true; } } return false; } static bool dsv4_token_has_seq(const llama_batch & batch, int32_t i, llama_seq_id seq_id) { if (batch.n_seq_id == nullptr || batch.seq_id == nullptr || batch.seq_id[i] == nullptr) { return seq_id == 0; } for (int32_t s = 0; s < batch.n_seq_id[i]; ++s) { if (batch.seq_id[i][s] == seq_id) { return true; } } return false; } static std::vector dsv4_batch_unique_seq_ids(const llama_batch & batch) { std::vector seq_ids; std::unordered_set seen; if (batch.n_tokens <= 0 || batch.n_seq_id == nullptr || batch.seq_id == nullptr) { seq_ids.push_back(0); return seq_ids; } for (int32_t i = 0; i < batch.n_tokens; ++i) { if (batch.n_seq_id[i] <= 0 || batch.seq_id[i] == nullptr) { continue; } for (int32_t s = 0; s < batch.n_seq_id[i]; ++s) { const llama_seq_id seq_id = batch.seq_id[i][s]; if (seen.insert(seq_id).second) { seq_ids.push_back(seq_id); } } } if (seq_ids.empty()) { seq_ids.push_back(0); } return seq_ids; } static int64_t dsv4_stream_offset(uint32_t n_stream, llama_seq_id seq_id, uint32_t size) { if (n_stream <= 1) { return 0; } if (seq_id < 0 || (uint32_t) seq_id >= n_stream) { LLAMA_LOG_ERROR("%s: DSV4 seq_id %d is outside stream range %u\n", __func__, seq_id, n_stream); return -1; } return (int64_t) seq_id*size; } static int64_t dsv4_comp_graph_n_stream(const llama_batch & batch, uint32_t n_stream) { if (n_stream <= 1) { return 1; } const std::vector seq_ids = dsv4_batch_unique_seq_ids(batch); if (seq_ids.size() <= 1 || dsv4_batch_has_coupled(batch)) { return 1; } return (int64_t) seq_ids.size(); } static std::vector dsv4_build_stream_seq_ids( const llama_batch & batch, uint32_t n_stream) { if (n_stream <= 1) { return { 0 }; } const std::vector seq_ids = dsv4_batch_unique_seq_ids(batch); if (seq_ids.size() <= 1 || dsv4_batch_has_coupled(batch)) { return { seq_ids.empty() ? 0 : seq_ids.front() }; } return seq_ids; } static llama_context::dsv4_runtime::slot_info dsv4_build_comp_sinfo( const llama_batch & batch, uint32_t n_stream) { llama_context::dsv4_runtime::slot_info sinfo; const std::vector seq_ids = dsv4_build_stream_seq_ids(batch, n_stream); const int64_t graph_n_stream = (int64_t) seq_ids.size(); bool have_stream = false; sinfo.s0 = INT_MAX; sinfo.s1 = 0; sinfo.resize((size_t) std::max(1, graph_n_stream)); for (int64_t s = 0; s < graph_n_stream; ++s) { const llama_seq_id seq_id = seq_ids[(size_t) s]; const int64_t strm = dsv4_stream_offset(n_stream, seq_id, 1); if (strm < 0) { continue; } sinfo.strm[(size_t) s] = (llama_seq_id) strm; sinfo.idxs[(size_t) s].assign(1, 0); sinfo.s0 = std::min(sinfo.s0, (int32_t) strm); sinfo.s1 = std::max(sinfo.s1, (int32_t) strm); have_stream = true; } if (!have_stream) { sinfo.resize(1); sinfo.strm[0] = 0; sinfo.idxs[0].assign(1, 0); sinfo.s0 = 0; sinfo.s1 = 0; } if (n_stream > 1 && sinfo.s1 - sinfo.s0 + 1 != (int32_t) sinfo.n_stream()) { LLAMA_LOG_ERROR("%s: DSV4 compressed streams are not contiguous in batch\n", __func__); } return sinfo; } static llama_context::dsv4_runtime::slot_info dsv4_build_raw_read_sinfo( const llama_context::dsv4_runtime::slot_info & sinfo_write, const llama_batch & batch, uint32_t n_stream) { if (!dsv4_batch_has_coupled(batch)) { return sinfo_write; } const llama_seq_id seq_id = (batch.n_seq_id != nullptr && batch.seq_id != nullptr && batch.n_tokens > 0 && batch.n_seq_id[0] > 0 && batch.seq_id[0] != nullptr) ? batch.seq_id[0][0] : 0; const int64_t strm = dsv4_stream_offset(n_stream, seq_id, 1); if (strm < 0) { return {}; } size_t i_stream = 0; for (; i_stream < sinfo_write.n_stream(); ++i_stream) { if ((int64_t) sinfo_write.strm[i_stream] == strm) { break; } } if (i_stream == sinfo_write.n_stream()) { LLAMA_LOG_ERROR("%s: DSV4 raw write stream not found for coupled read\n", __func__); return {}; } llama_context::dsv4_runtime::slot_info sinfo; sinfo.resize(1); sinfo.strm[0] = sinfo_write.strm[i_stream]; sinfo.idxs[0] = sinfo_write.idxs[i_stream]; sinfo.s0 = (int32_t) strm; sinfo.s1 = sinfo.s0; return sinfo; } static bool dsv4_validate_batch_seq_ids( const llama_context & lctx, const llama_batch & batch) { if (batch.n_tokens <= 0 || batch.n_seq_id == nullptr || batch.seq_id == nullptr) { return true; } const uint32_t n_stream = std::max(1, lctx.cparams.n_seq_max); for (int32_t i = 0; i < batch.n_tokens; ++i) { if (batch.n_seq_id[i] <= 0 || batch.seq_id[i] == nullptr) { LLAMA_LOG_ERROR("%s: DSV4 token %d is missing seq_id ownership\n", __func__, i); return false; } for (int32_t s = 0; s < batch.n_seq_id[i]; ++s) { const llama_seq_id seq_id = batch.seq_id[i][s]; if (seq_id < 0 || (uint32_t) seq_id >= n_stream) { LLAMA_LOG_ERROR("%s: DSV4 token %d seq_id %d is outside n_seq_max=%u\n", __func__, i, seq_id, n_stream); return false; } } } return true; } static bool dsv4_build_raw_context( const llama_context & lctx, const llama_batch & batch, llama_context::dsv4_runtime::raw_context & raw) { raw = {}; const uint32_t n_stream = std::max(1, lctx.cparams.n_seq_max); const std::vector write_seq_ids = dsv4_build_stream_seq_ids(batch, n_stream); raw.sinfo_write = dsv4_build_comp_sinfo(batch, n_stream); raw.sinfo_read = dsv4_build_raw_read_sinfo(raw.sinfo_write, batch, n_stream); raw.graph_n_stream = (int64_t) raw.sinfo_write.n_stream(); std::vector read_seq_ids = write_seq_ids; if (dsv4_batch_has_coupled(batch)) { const llama_seq_id coupled_seq_id = (batch.n_seq_id != nullptr && batch.seq_id != nullptr && batch.n_tokens > 0 && batch.n_seq_id[0] > 0 && batch.seq_id[0] != nullptr) ? batch.seq_id[0][0] : 0; read_seq_ids.assign(1, coupled_seq_id); } if (batch.n_tokens <= 0) { return true; } const llama_kv_cache & kv = lctx.kv_self; if (kv.head + batch.n_tokens > (int32_t) kv.size) { LLAMA_LOG_ERROR("%s: DSV4 raw write slots [%d, %d) are outside kv cache size %u\n", __func__, kv.head, kv.head + batch.n_tokens, kv.size); return false; } // compacted layers address raw K rows through [sinks | window] geometry rather than by cell const bool compacted = kv.any_compacted(); if (compacted) { if (kv.head_swa + (uint32_t) batch.n_tokens > kv.size_swa) { LLAMA_LOG_ERROR("%s: DSV4 compacted raw write rows [%u, %u) are outside size_swa %u\n", __func__, kv.head_swa, kv.head_swa + (uint32_t) batch.n_tokens, kv.size_swa); return false; } if (batch.pos != nullptr && batch.n_tokens > 0 && kv.pos_base_swa + (llama_pos) (kv.head_swa - kv.sink_rows) != batch.pos[0]) { LLAMA_LOG_ERROR("%s: DSV4 compacted write row %u disagrees with batch position %d (base %d)\n", __func__, kv.head_swa, batch.pos[0], kv.pos_base_swa); return false; } } raw.write_counts.push_back(batch.n_tokens); for (int32_t i = 0; i < batch.n_tokens; ++i) { const int32_t slot = kv.head + i; const llama_kv_cell & cell = kv.cells[(size_t) slot]; if (batch.pos != nullptr && cell.pos != batch.pos[i]) { LLAMA_LOG_ERROR("%s: DSV4 raw write slot %d pos mismatch: cell=%d batch=%d\n", __func__, slot, cell.pos, batch.pos[i]); return false; } raw.write_src_idxs.push_back(i); raw.write_dst_idxs.push_back(compacted ? (int32_t) kv.head_swa + i : slot); } raw.n_kv = 0; for (size_t s = 0; s < raw.sinfo_read.n_stream(); ++s) { const llama_seq_id seq_id = read_seq_ids[s]; raw.sinfo_read.idxs[s].clear(); int32_t count = 0; for (uint32_t slot = 0; slot < kv.size; ++slot) { const llama_kv_cell & cell = kv.cells[slot]; if (cell.is_empty() || cell.pos < 0) { continue; } if (!cell.has_seq_id(seq_id)) { continue; } if (compacted && cell.pos < kv.pos_base_swa) { // rows before the window base were overwritten by compaction continue; } const uint32_t row = compacted ? kv.sink_rows + (uint32_t) (cell.pos - kv.pos_base_swa) : slot; raw.sinfo_read.idxs[s].push_back(row); raw.read_dst_idxs.push_back((int32_t) row); ++count; } raw.read_counts.push_back(count); raw.n_kv = std::max(raw.n_kv, count); } if (raw.read_counts.empty()) { raw.read_counts.push_back(0); } for (size_t s = 0; s < raw.sinfo_write.n_stream(); ++s) { const llama_seq_id seq_id = write_seq_ids[s]; raw.sinfo_write.idxs[s].clear(); for (int32_t i = 0; i < batch.n_tokens; ++i) { if (!dsv4_token_has_seq(batch, i, seq_id)) { continue; } raw.sinfo_write.idxs[s].push_back((uint32_t) (kv.head + i)); } } if (raw.sinfo_write.n_stream() > 1) { std::vector write_src_idxs; std::vector write_dst_idxs; const size_t rows_per_stream = raw.sinfo_write.size(); for (size_t s = 0; s < raw.sinfo_write.n_stream(); ++s) { if (raw.sinfo_write.idxs[s].size() != rows_per_stream) { LLAMA_LOG_ERROR("%s: DSV4 packed batch has unequal raw-write rows per stream\n", __func__); return false; } for (int32_t i = 0; i < batch.n_tokens; ++i) { if (dsv4_token_has_seq(batch, i, write_seq_ids[s])) { write_src_idxs.push_back(i); } } for (uint32_t slot : raw.sinfo_write.idxs[s]) { write_dst_idxs.push_back((int32_t) slot); } } raw.write_src_idxs = std::move(write_src_idxs); raw.write_dst_idxs = std::move(write_dst_idxs); } // The graph exposes a rectangular raw-key view. Repeat the last valid row // for shorter streams; the corresponding mask entries remain -INFINITY. // This preserves the logical visibility while allowing one get_rows op to // serve all streams. if (raw.n_kv > 0) { raw.read_dst_idxs.clear(); const size_t read_rows = GGML_PAD((size_t) raw.n_kv, 256u); for (size_t s = 0; s < raw.sinfo_read.n_stream(); ++s) { const auto & rows = raw.sinfo_read.idxs[s]; for (uint32_t slot : rows) { raw.read_dst_idxs.push_back((int32_t) slot); } const int32_t pad = rows.empty() ? 0 : (int32_t) rows.back(); for (size_t i = rows.size(); i < read_rows; ++i) { raw.read_dst_idxs.push_back(pad); } } } return true; } static llama_context::dsv4_runtime::comp_context dsv4_build_comp_context( const llama_batch & batch, uint32_t n_stream, int64_t n_kv) { llama_context::dsv4_runtime::comp_context ctx; ctx.sinfo = dsv4_build_comp_sinfo(batch, n_stream); ctx.graph_n_stream = dsv4_comp_graph_n_stream(batch, n_stream); ctx.n_kv = n_kv; return ctx; } static llama_context::dsv4_runtime::comp_plan dsv4_build_reserve_comp_plan( const llama_batch & batch, uint32_t ratio, bool overlap, uint32_t state_size, uint32_t kv_size, uint32_t n_stream) { llama_context::dsv4_runtime::comp_plan plan; plan.n_visible.resize((size_t) batch.n_tokens, (int32_t) kv_size); plan.n_stream = dsv4_comp_graph_n_stream(batch, n_stream); plan.n_kv = kv_size; if (batch.n_tokens == 0) { return plan; } uint32_t n_seqs = 1; uint32_t n_seq_tokens = 1; dsv4_batch_shape(batch, n_seqs, n_seq_tokens); plan.n_visible.assign((size_t) batch.n_tokens, 0); const uint64_t n_blocks_u64 = (uint64_t) n_seqs*((n_seq_tokens + ratio - 1)/ratio); const size_t n_blocks = (size_t) std::max(1, n_blocks_u64); GGML_ASSERT((uint64_t) n_blocks == std::max(1, n_blocks_u64)); const uint64_t state_rows = (uint64_t) state_size*(uint64_t) n_stream; const size_t n_persist = (size_t) std::min((uint64_t) batch.n_tokens, state_rows); plan.state_pos.resize((size_t) batch.n_tokens); plan.state_delta_src_idxs.resize((size_t) batch.n_tokens); plan.state_delta_dst_idxs.resize((size_t) batch.n_tokens); plan.state_persist_src_idxs.resize(n_persist); plan.state_persist_dst_idxs.resize(n_persist); plan.state_read_idxs.resize((overlap ? 2u : 1u)*ratio*n_blocks); plan.state_write_idxs.resize(n_blocks); plan.state_write_pos.resize(n_blocks); return plan; } static uint32_t dsv4_cache_kv_size(const std::vector & tensors) { for (ggml_tensor * tensor : tensors) { if (tensor != nullptr) { return (uint32_t) tensor->ne[1]; } } return 0; } static uint32_t dsv4_cache_state_size(const std::vector & tensors) { for (ggml_tensor * tensor : tensors) { if (tensor != nullptr) { return (uint32_t) tensor->ne[1]; } } return 0; } static bool dsv4_validate_comp_plan( const char * tag, const llama_batch & batch, const llama_context::dsv4_runtime::comp_plan & plan, uint32_t ratio, bool overlap, uint32_t state_size, uint32_t kv_size, uint32_t n_stream) { const int64_t max_state_read_idx = (int64_t) state_size*n_stream + batch.n_tokens + (overlap ? 0 : -1); if (plan.n_visible.size() != (size_t) std::max(0, batch.n_tokens)) { LLAMA_LOG_ERROR("%s: DSV4 %s plan n_visible size mismatch: got=%zu expected=%d\n", __func__, tag, plan.n_visible.size(), std::max(0, batch.n_tokens)); return false; } if (plan.state_pos.size() > (size_t) std::max(0, batch.n_tokens)) { LLAMA_LOG_ERROR("%s: DSV4 %s plan has too many state_pos rows: %zu > %d\n", __func__, tag, plan.state_pos.size(), std::max(0, batch.n_tokens)); return false; } if (plan.state_delta_src_idxs.size() != plan.state_pos.size() || plan.state_delta_dst_idxs.size() != plan.state_pos.size()) { LLAMA_LOG_ERROR("%s: DSV4 %s delta row metadata mismatch: state=%zu src=%zu dst=%zu\n", __func__, tag, plan.state_pos.size(), plan.state_delta_src_idxs.size(), plan.state_delta_dst_idxs.size()); return false; } if (plan.state_persist_src_idxs.size() != plan.state_persist_dst_idxs.size()) { LLAMA_LOG_ERROR("%s: DSV4 %s persist idx size mismatch: src=%zu dst=%zu\n", __func__, tag, plan.state_persist_src_idxs.size(), plan.state_persist_dst_idxs.size()); return false; } if (plan.state_write_idxs.size() != plan.state_write_pos.size()) { LLAMA_LOG_ERROR("%s: DSV4 %s write idx size mismatch: idxs=%zu pos=%zu\n", __func__, tag, plan.state_write_idxs.size(), plan.state_write_pos.size()); return false; } for (size_t i = 0; i < plan.n_visible.size(); ++i) { const int32_t n_visible = plan.n_visible[i]; if (n_visible < 0 || (uint32_t) n_visible > kv_size) { LLAMA_LOG_ERROR("%s: DSV4 %s n_visible[%zu]=%d exceeds kv_size=%u\n", __func__, tag, i, n_visible, kv_size); return false; } } for (size_t i = 0; i < plan.state_pos.size(); ++i) { const int64_t pos = plan.state_pos[i]; if (pos < 0 || pos >= (int64_t) ratio) { LLAMA_LOG_ERROR("%s: DSV4 %s state_pos[%zu]=%lld outside ratio=%u\n", __func__, tag, i, (long long) pos, ratio); return false; } const int64_t src = plan.state_delta_src_idxs[i]; const int64_t dst = plan.state_delta_dst_idxs[i]; if (src < 0 || src >= batch.n_tokens || dst < 0 || (uint32_t) dst >= state_size*n_stream) { LLAMA_LOG_ERROR("%s: DSV4 %s delta row[%zu] src=%lld dst=%lld is outside the batch/state ring\n", __func__, tag, i, (long long) src, (long long) dst); return false; } } for (size_t i = 0; i < plan.state_persist_src_idxs.size(); ++i) { const int64_t src = plan.state_persist_src_idxs[i]; const int64_t dst = plan.state_persist_dst_idxs[i]; if (src < 0 || src >= batch.n_tokens) { LLAMA_LOG_ERROR("%s: DSV4 %s persist src[%zu]=%lld outside current batch rows=%d\n", __func__, tag, i, (long long) src, batch.n_tokens); return false; } if (dst < 0 || (uint32_t) dst >= state_size*n_stream) { LLAMA_LOG_ERROR("%s: DSV4 %s persist dst[%zu]=%lld outside state_size*n_stream=%u\n", __func__, tag, i, (long long) dst, state_size*n_stream); return false; } } for (size_t i = 0; i < plan.state_read_idxs.size(); ++i) { const int64_t idx = plan.state_read_idxs[i]; if (idx < 0 || idx > max_state_read_idx) { LLAMA_LOG_ERROR("%s: DSV4 %s read idx[%zu]=%lld outside max source row=%lld\n", __func__, tag, i, (long long) idx, (long long) max_state_read_idx); return false; } } for (size_t i = 0; i < plan.state_write_idxs.size(); ++i) { const int64_t idx = plan.state_write_idxs[i]; if (idx < 0 || (uint32_t) idx >= kv_size*n_stream) { LLAMA_LOG_ERROR("%s: DSV4 %s write idx[%zu]=%lld outside kv_size*n_stream=%u\n", __func__, tag, i, (long long) idx, kv_size*n_stream); return false; } } if (plan.n_kv == 0 || (uint32_t) plan.n_kv > kv_size) { LLAMA_LOG_ERROR("%s: DSV4 %s plan n_kv=%lld outside kv_size=%u\n", __func__, tag, (long long) plan.n_kv, kv_size); return false; } return true; } static llama_context::dsv4_runtime::comp_plan dsv4_build_comp_plan( const llama_batch & batch, uint32_t ratio, bool overlap, uint32_t state_size, uint32_t kv_size, uint32_t n_stream) { llama_context::dsv4_runtime::comp_plan plan; plan.n_visible.resize((size_t) batch.n_tokens); plan.n_stream = dsv4_comp_graph_n_stream(batch, n_stream); if (n_stream <= 1 && dsv4_batch_unique_seq_ids(batch).size() > 1) { LLAMA_LOG_ERROR("%s: DSV4 single compressed stream cannot serve multiple sequences\n", __func__); return plan; } const int64_t state_rows = (int64_t) state_size*n_stream; struct persist_row { int32_t dst; int32_t src; llama_pos pos; }; std::vector persist_rows; std::vector overlap_prev_reads; std::vector overlap_cur_reads; std::map, int32_t> curr_token_idx_map; for (int32_t i = 0; i < batch.n_tokens; ++i) { const int32_t n_token_seqs = batch.n_seq_id != nullptr && batch.seq_id != nullptr && batch.seq_id[i] != nullptr ? batch.n_seq_id[i] : 1; for (int32_t s = 0; s < n_token_seqs; ++s) { const llama_seq_id seq_id = batch.n_seq_id != nullptr && batch.seq_id != nullptr && batch.seq_id[i] != nullptr ? batch.seq_id[i][s] : 0; curr_token_idx_map[std::make_pair(seq_id, batch.pos[i])] = i; } } const auto state_source_idx = [&](llama_seq_id seq_id, llama_pos pos) -> int32_t { if (pos < 0) { return (int32_t) (state_rows + batch.n_tokens); } const auto it = curr_token_idx_map.find(std::make_pair(seq_id, pos)); if (it != curr_token_idx_map.end()) { return (int32_t) (state_rows + it->second); } const int64_t stream_off = dsv4_stream_offset(n_stream, seq_id, state_size); GGML_ASSERT(stream_off >= 0); return (int32_t) (stream_off + pos%state_size); }; for (int32_t i = 0; i < batch.n_tokens; ++i) { const llama_pos pos = batch.pos[i]; if (pos < 0) { continue; } plan.state_pos.push_back((int32_t) (pos%ratio)); const llama_seq_id delta_seq_id = batch.n_seq_id != nullptr && batch.seq_id != nullptr && batch.seq_id[i] != nullptr && batch.n_seq_id[i] > 0 ? batch.seq_id[i][0] : 0; plan.state_delta_src_idxs.push_back(i); plan.state_delta_dst_idxs.push_back((int32_t) ( dsv4_stream_offset(n_stream, delta_seq_id, state_size) + pos%state_size)); const int64_t n_visible = (int64_t) (pos + 1)/ratio; plan.n_visible[(size_t) i] = (int32_t) n_visible; plan.n_kv = std::max(plan.n_kv, n_visible); const int32_t n_token_seqs = batch.n_seq_id != nullptr && batch.seq_id != nullptr && batch.seq_id[i] != nullptr ? batch.n_seq_id[i] : 1; for (int32_t s = 0; s < n_token_seqs; ++s) { const llama_seq_id seq_id = batch.n_seq_id != nullptr && batch.seq_id != nullptr && batch.seq_id[i] != nullptr ? batch.seq_id[i][s] : 0; const int64_t stream_off = dsv4_stream_offset(n_stream, seq_id, state_size); const int32_t state_idx = (int32_t) (stream_off + pos%state_size); const auto it = std::find_if(persist_rows.begin(), persist_rows.end(), [state_idx](const persist_row & row) { return row.dst == state_idx; }); if (it == persist_rows.end()) { persist_rows.push_back({ state_idx, i, pos }); } else if (pos > it->pos) { it->src = i; it->pos = pos; } if ((pos + 1) % ratio != 0) { continue; } const llama_pos source_start = pos + 1 - ratio; const int64_t cache_off = dsv4_stream_offset(n_stream, seq_id, kv_size); plan.state_write_idxs.push_back(cache_off + pos/ratio); plan.state_write_pos.push_back((int32_t) source_start); if (overlap) { const llama_pos prev_start = source_start - ratio; for (uint32_t j = 0; j < ratio; ++j) { overlap_prev_reads.push_back(state_source_idx(seq_id, prev_start + j)); } for (uint32_t j = 0; j < ratio; ++j) { overlap_cur_reads.push_back(state_source_idx(seq_id, source_start + j)); } } else { for (uint32_t j = 0; j < ratio; ++j) { plan.state_read_idxs.push_back(state_source_idx(seq_id, source_start + j)); } } } } if (ratio == llama_context::dsv4_runtime::CSA_RATIO && plan.state_write_idxs.empty() && !plan.state_pos.empty()) { const llama_seq_id seq_id0 = batch.n_seq_id != nullptr && batch.seq_id != nullptr && batch.n_seq_id[0] > 0 && batch.seq_id[0] != nullptr ? batch.seq_id[0][0] : 0; const uint32_t source_idx = (uint32_t) state_source_idx(seq_id0, batch.pos[0]); const int64_t cache_off = std::max(0, dsv4_stream_offset(n_stream, seq_id0, kv_size)); plan.state_write_idxs.push_back(cache_off + (int64_t) kv_size - 1); plan.state_write_pos.push_back(0); if (overlap) { for (uint32_t j = 0; j < ratio; ++j) { overlap_prev_reads.push_back(source_idx); overlap_cur_reads.push_back(source_idx); } } else { for (uint32_t j = 0; j < ratio; ++j) { plan.state_read_idxs.push_back(source_idx); } } } if (overlap) { plan.state_read_idxs.reserve(overlap_prev_reads.size() + overlap_cur_reads.size()); plan.state_read_idxs.insert(plan.state_read_idxs.end(), overlap_prev_reads.begin(), overlap_prev_reads.end()); plan.state_read_idxs.insert(plan.state_read_idxs.end(), overlap_cur_reads.begin(), overlap_cur_reads.end()); } plan.n_kv = GGML_PAD(plan.n_kv, 256u); std::sort(persist_rows.begin(), persist_rows.end(), [](const persist_row & a, const persist_row & b) { return a.dst < b.dst; }); for (const persist_row & row : persist_rows) { plan.state_persist_src_idxs.push_back(row.src); plan.state_persist_dst_idxs.push_back(row.dst); } if (plan.n_kv == 0) { plan.n_kv = GGML_PAD(1, 256u); } return plan; } template static void dsv4_set_input_tensor(ggml_tensor * tensor, const std::vector & values) { if (tensor == nullptr || tensor->buffer == nullptr || values.empty()) { return; } ggml_backend_tensor_set(tensor, values.data(), 0, values.size()*sizeof(T)); } static void dsv4_set_mask_tensor( ggml_tensor * tensor, const llama_context::dsv4_runtime::comp_plan & plan, int32_t n_tokens) { if (tensor == nullptr) { return; } if (tensor->buffer == nullptr) { return; } const int64_t width = tensor->ne[0]; const int64_t height = tensor->ne[1]; auto type = tensor->type; GGML_ASSERT(type == GGML_TYPE_F16 || type == GGML_TYPE_F32); //printf("%s: preparing mask %s of type %s with %ld x %ld entries\n", __func__, tensor->name, ggml_type_name(type), tensor->ne[0], tensor->ne[1]); if (type == GGML_TYPE_F16) { auto h_inf = ggml_fp32_to_fp16(-INFINITY); auto h_zero = ggml_fp32_to_fp16(0.0f); std::vector storage((size_t) width*height, h_inf); for (int32_t i = 0; i < n_tokens; ++i) { const int32_t n_visible = i < (int32_t) plan.n_visible.size() ? plan.n_visible[(size_t) i] : 0; //if (i == 0) printf(" n_visible = %d\n", n_visible); for (int32_t j = 0; j < n_visible && j < width; ++j) { storage[(size_t) i*width + j] = h_zero; } } ggml_backend_tensor_set(tensor, storage.data(), 0, storage.size()*sizeof(ggml_fp16_t)); } else { std::vector storage((size_t) width*height, -INFINITY); for (int32_t i = 0; i < n_tokens; ++i) { const int32_t n_visible = i < (int32_t) plan.n_visible.size() ? plan.n_visible[(size_t) i] : 0; for (int32_t j = 0; j < n_visible && j < width; ++j) { storage[(size_t) i*width + j] = 0.0f; } } ggml_backend_tensor_set(tensor, storage.data(), 0, storage.size()*sizeof(float)); } } bool llama_context::ensure_dsv4_cache_tensors() { const int32_t n_layer = model.hparams.n_layer; const int64_t n_embd_head = model.hparams.n_embd_head_k(0); const int64_t n_indexer_head = model.hparams.indexer_head_size; const uint32_t n_stream = std::max(1, cparams.n_seq_max); const uint32_t csa_kv = GGML_PAD(dsv4_comp_size(cparams.n_ctx, dsv4_runtime::CSA_RATIO), 256u); const uint32_t hca_kv = GGML_PAD(dsv4_comp_size(cparams.n_ctx, dsv4_runtime::HCA_RATIO), 256u); if (!dsv4_validate_cache_type(kv_self.type_k, n_embd_head, "raw/CSA/HCA") || !dsv4_validate_cache_type(cparams.idx_type_k, n_indexer_head, "LID")) { return false; } if (dsv4.cache.cache_ctx != nullptr && (int32_t) dsv4.cache.csa_k.size() == n_layer && dsv4.cache.n_stream == n_stream) { return true; } free_dsv4_cache_tensors(); ggml_init_params params = { /*.mem_size =*/ (size_t) (16 * std::max(1, n_layer)) * ggml_tensor_overhead(), /*.mem_buffer =*/ nullptr, /*.no_alloc =*/ true, }; dsv4.cache.cache_ctx = ggml_init(params); if (dsv4.cache.cache_ctx == nullptr) { LLAMA_LOG_ERROR("%s: failed to allocate DSV4 cache context\n", __func__); return false; } auto & cache = dsv4.cache; cache.n_stream = n_stream; cache.csa_k.resize((size_t) n_layer, nullptr); cache.hca_k.resize((size_t) n_layer, nullptr); cache.lid_k.resize((size_t) n_layer, nullptr); cache.csa_state_kv.resize((size_t) n_layer, nullptr); cache.csa_state_score.resize((size_t) n_layer, nullptr); cache.hca_state_kv.resize((size_t) n_layer, nullptr); cache.hca_state_score.resize((size_t) n_layer, nullptr); cache.lid_state_kv.resize((size_t) n_layer, nullptr); cache.lid_state_score.resize((size_t) n_layer, nullptr); auto alloc_tensor = [&](ggml_tensor * tensor, ggml_backend_buffer_type_t buft) -> bool { const size_t tensor_bytes = ggml_backend_buft_get_alloc_size(buft, tensor); ggml_backend_buffer_t buf = ggml_backend_buft_alloc_buffer(buft, tensor_bytes); if (buf == nullptr) { return false; } ggml_backend_buffer_set_usage(buf, GGML_BACKEND_BUFFER_USAGE_COMPUTE); ggml_backend_tensor_alloc(buf, tensor, ggml_backend_buffer_get_base(buf)); ggml_backend_buffer_clear(buf, 0); cache.cache_bufs.push_back(buf); return true; }; for (int32_t il = 0; il < n_layer; ++il) { const uint32_t ratio = model.hparams.dsv4_compress_ratios[(size_t) il]; ggml_backend_buffer_type_t buft = llama_dsv4_layer_buft(*this, il); if (ratio == dsv4_runtime::CSA_RATIO) { cache.csa_k[(size_t) il] = ggml_new_tensor_3d(cache.cache_ctx, kv_self.type_k, n_embd_head, csa_kv*n_stream, 1); cache.lid_k[(size_t) il] = ggml_new_tensor_3d(cache.cache_ctx, cparams.idx_type_k, n_indexer_head, csa_kv*n_stream, 1); cache.csa_state_kv[(size_t) il] = ggml_new_tensor_2d(cache.cache_ctx, GGML_TYPE_F32, 2*n_embd_head, 2*dsv4_runtime::CSA_RATIO*n_stream); cache.csa_state_score[(size_t) il] = ggml_new_tensor_2d(cache.cache_ctx, GGML_TYPE_F32, 2*n_embd_head, 2*dsv4_runtime::CSA_RATIO*n_stream); cache.lid_state_kv[(size_t) il] = ggml_new_tensor_2d(cache.cache_ctx, GGML_TYPE_F32, 2*n_indexer_head, 2*dsv4_runtime::CSA_RATIO*n_stream); cache.lid_state_score[(size_t) il] = ggml_new_tensor_2d(cache.cache_ctx, GGML_TYPE_F32, 2*n_indexer_head, 2*dsv4_runtime::CSA_RATIO*n_stream); if (!alloc_tensor(cache.csa_k[(size_t) il], buft) || !alloc_tensor(cache.lid_k[(size_t) il], buft) || !alloc_tensor(cache.csa_state_kv[(size_t) il], buft) || !alloc_tensor(cache.csa_state_score[(size_t) il], buft) || !alloc_tensor(cache.lid_state_kv[(size_t) il], buft) || !alloc_tensor(cache.lid_state_score[(size_t) il], buft)) { LLAMA_LOG_ERROR("%s: failed to allocate DSV4 CSA/LID buffers for layer %d\n", __func__, il); free_dsv4_cache_tensors(); return false; } } else if (ratio == dsv4_runtime::HCA_RATIO) { cache.hca_k[(size_t) il] = ggml_new_tensor_3d(cache.cache_ctx, kv_self.type_k, n_embd_head, hca_kv*n_stream, 1); cache.hca_state_kv[(size_t) il] = ggml_new_tensor_2d(cache.cache_ctx, GGML_TYPE_F32, n_embd_head, dsv4_runtime::HCA_RATIO*n_stream); cache.hca_state_score[(size_t) il] = ggml_new_tensor_2d(cache.cache_ctx, GGML_TYPE_F32, n_embd_head, dsv4_runtime::HCA_RATIO*n_stream); if (!alloc_tensor(cache.hca_k[(size_t) il], buft) || !alloc_tensor(cache.hca_state_kv[(size_t) il], buft) || !alloc_tensor(cache.hca_state_score[(size_t) il], buft)) { LLAMA_LOG_ERROR("%s: failed to allocate DSV4 HCA buffers for layer %d\n", __func__, il); free_dsv4_cache_tensors(); return false; } } } auto bytes = [](const auto & tensors) { size_t total = 0; for (const ggml_tensor * tensor : tensors) { if (tensor != nullptr) { total += ggml_nbytes(tensor); } } return total; }; const size_t csa_k_bytes = bytes(cache.csa_k); const size_t hca_k_bytes = bytes(cache.hca_k); const size_t lid_k_bytes = bytes(cache.lid_k); const size_t csa_state_bytes = bytes(cache.csa_state_kv) + bytes(cache.csa_state_score); const size_t hca_state_bytes = bytes(cache.hca_state_kv) + bytes(cache.hca_state_score); const size_t lid_state_bytes = bytes(cache.lid_state_kv) + bytes(cache.lid_state_score); LLAMA_LOG_INFO("%s: DSV4 cache: CSA K=%7.2f MiB (%s), HCA K=%7.2f MiB (%s), LID K=%7.2f MiB (%s), states=%7.2f MiB, total=%7.2f MiB, streams=%u\n", __func__, (float) csa_k_bytes / (1024.0f * 1024.0f), ggml_type_name(kv_self.type_k), (float) hca_k_bytes / (1024.0f * 1024.0f), ggml_type_name(kv_self.type_k), (float) lid_k_bytes / (1024.0f * 1024.0f), ggml_type_name(cparams.idx_type_k), (float) (csa_state_bytes + hca_state_bytes + lid_state_bytes) / (1024.0f * 1024.0f), (float) (csa_k_bytes + hca_k_bytes + lid_k_bytes + csa_state_bytes + hca_state_bytes + lid_state_bytes) / (1024.0f * 1024.0f), n_stream); return true; } void llama_context::free_dsv4_cache_tensors() { auto release_vector = [](auto & v) { using vec_type = std::decay_t; vec_type().swap(v); }; for (ggml_backend_buffer_t buf : dsv4.cache.cache_bufs) { if (buf != nullptr) { ggml_backend_buffer_free(buf); } } release_vector(dsv4.cache.cache_bufs); release_vector(dsv4.cache.csa_k); release_vector(dsv4.cache.hca_k); release_vector(dsv4.cache.lid_k); release_vector(dsv4.cache.csa_state_kv); release_vector(dsv4.cache.csa_state_score); release_vector(dsv4.cache.hca_state_kv); release_vector(dsv4.cache.hca_state_score); release_vector(dsv4.cache.lid_state_kv); release_vector(dsv4.cache.lid_state_score); dsv4.cache.n_stream = 1; if (dsv4.cache.cache_ctx != nullptr) { ggml_free(dsv4.cache.cache_ctx); dsv4.cache.cache_ctx = nullptr; } } void llama_reset_dsv4_state(llama_context * ctx, int32_t seq_id) { if (ctx == nullptr) { return; } const uint32_t n_stream = std::max(1, ctx->dsv4.cache.n_stream); if (seq_id >= (llama_seq_id) n_stream) { LLAMA_LOG_ERROR("%s: DSV4 seq_id %d is outside stream range %u\n", __func__, seq_id, n_stream); return; } if (seq_id < 0) { for (ggml_backend_buffer_t buf : ctx->dsv4.cache.cache_bufs) { ggml_backend_buffer_clear(buf, 0); } return; } auto clear_tensor = [seq_id, n_stream](ggml_tensor * tensor) { if (tensor == nullptr) { return; } GGML_ASSERT(tensor->ne[1] % n_stream == 0); const size_t row_bytes = tensor->nb[1]; const size_t rows_per_stream = (size_t) tensor->ne[1] / n_stream; const size_t offset = (size_t) seq_id * rows_per_stream * row_bytes; const size_t bytes = rows_per_stream * row_bytes; std::vector zeros(bytes, 0); ggml_backend_tensor_set(tensor, zeros.data(), offset, bytes); }; for (ggml_tensor * tensor : ctx->dsv4.cache.csa_k) clear_tensor(tensor); for (ggml_tensor * tensor : ctx->dsv4.cache.hca_k) clear_tensor(tensor); for (ggml_tensor * tensor : ctx->dsv4.cache.lid_k) clear_tensor(tensor); for (ggml_tensor * tensor : ctx->dsv4.cache.csa_state_kv) clear_tensor(tensor); for (ggml_tensor * tensor : ctx->dsv4.cache.csa_state_score) clear_tensor(tensor); for (ggml_tensor * tensor : ctx->dsv4.cache.hca_state_kv) clear_tensor(tensor); for (ggml_tensor * tensor : ctx->dsv4.cache.hca_state_score) clear_tensor(tensor); for (ggml_tensor * tensor : ctx->dsv4.cache.lid_state_kv) clear_tensor(tensor); for (ggml_tensor * tensor : ctx->dsv4.cache.lid_state_score) clear_tensor(tensor); } static std::vector dsv4_state_tensors(const llama_context & ctx) { std::vector tensors; const auto append = [&tensors](const std::vector & group) { for (ggml_tensor * tensor : group) { if (tensor != nullptr) { tensors.push_back(tensor); } } }; append(ctx.dsv4.cache.csa_state_kv); append(ctx.dsv4.cache.csa_state_score); append(ctx.dsv4.cache.hca_state_kv); append(ctx.dsv4.cache.hca_state_score); append(ctx.dsv4.cache.lid_state_kv); append(ctx.dsv4.cache.lid_state_score); return tensors; } void llama_kv_cache::gpu_checkpoint::release_dsv4_per_step() { for (ggml_context * shadow_ctx : dsv4_per_step_shadow_ctxs) { ggml_free(shadow_ctx); } for (ggml_backend_buffer_t buffer : dsv4_per_step_shadow_bufs) { ggml_backend_buffer_free(buffer); } dsv4_per_step_shadow_ctxs.clear(); dsv4_per_step_shadow_bufs.clear(); dsv4_per_step_state.clear(); dsv4_per_step_state_shadow.clear(); dsv4_per_step_delta.clear(); dsv4_per_step_csa_src.clear(); dsv4_per_step_csa_dst.clear(); dsv4_per_step_hca_src.clear(); dsv4_per_step_hca_dst.clear(); dsv4_per_step_lid_src.clear(); dsv4_per_step_lid_dst.clear(); dsv4_per_step_allocated = false; dsv4_per_step_saved = false; dsv4_per_step_max_tokens = 0; dsv4_per_step_base_bytes = 0; dsv4_per_step_delta_bytes = 0; } void llama_kv_cache::gpu_checkpoint::release_dsv4_snapshot() { for (ggml_context * shadow_ctx : dsv4_shadow_ctxs) { ggml_free(shadow_ctx); } for (ggml_backend_buffer_t buffer : dsv4_shadow_bufs) { ggml_backend_buffer_free(buffer); } dsv4_shadow_ctxs.clear(); dsv4_shadow_bufs.clear(); dsv4_state_data.clear(); dsv4_state_shadow.clear(); dsv4_shadow_allocated = false; dsv4_shadow_saved = false; } static bool dsv4_per_step_alloc(llama_context & ctx, int max_tokens) { auto & ckpt = ctx.kv_self.ckpt; const auto states = dsv4_state_tensors(ctx); if (states.empty() || max_tokens <= 0 || max_tokens > DSV4_PER_STEP_MAX_STATE_ROWS) { if (max_tokens > DSV4_PER_STEP_MAX_STATE_ROWS) { LLAMA_LOG_WARN("%s: DSV4 per-step supports at most %d verification rows; requested %d\n", __func__, DSV4_PER_STEP_MAX_STATE_ROWS, max_tokens); } return false; } if (ckpt.dsv4_per_step_allocated && ckpt.dsv4_per_step_max_tokens >= max_tokens && ckpt.dsv4_per_step_state.size() == states.size()) { return true; } ctx.kv_self.ckpt.release_dsv4_per_step(); ckpt.dsv4_per_step_state = states; ckpt.dsv4_per_step_state_shadow.assign(states.size(), nullptr); ckpt.dsv4_per_step_delta.assign(states.size(), nullptr); struct entry { size_t index; ggml_tensor * source; }; std::map> entries_by_buft; for (size_t i = 0; i < states.size(); ++i) { ggml_tensor * source = states[i]; if (source == nullptr || source->buffer == nullptr) { ctx.kv_self.ckpt.release_dsv4_per_step(); return false; } entries_by_buft[ggml_backend_buffer_get_type(source->buffer)].push_back({ i, source }); ckpt.dsv4_per_step_base_bytes += ggml_nbytes(source); ckpt.dsv4_per_step_delta_bytes += ggml_row_size(source->type, source->ne[0]) * (size_t) max_tokens; } for (auto & [buft, entries] : entries_by_buft) { ggml_init_params params = { /*.mem_size =*/ entries.size() * 3 * ggml_tensor_overhead(), /*.mem_buffer =*/ nullptr, /*.no_alloc =*/ true, }; ggml_context * graph_ctx = ggml_init(params); if (graph_ctx == nullptr) { ctx.kv_self.ckpt.release_dsv4_per_step(); return false; } for (const entry & item : entries) { ggml_tensor * shadow = ggml_dup_tensor(graph_ctx, item.source); for (int d = 0; d < GGML_MAX_DIMS; ++d) { shadow->nb[d] = item.source->nb[d]; } ggml_format_name(shadow, "dsv4_per_step_base_%zu", item.index); ggml_tensor * delta = ggml_new_tensor_2d(graph_ctx, item.source->type, item.source->ne[0], max_tokens); ggml_format_name(delta, "dsv4_per_step_delta_%zu", item.index); ckpt.dsv4_per_step_state_shadow[item.index] = shadow; ckpt.dsv4_per_step_delta[item.index] = delta; } ggml_backend_buffer_t buffer = ggml_backend_alloc_ctx_tensors_from_buft(graph_ctx, buft); if (buffer == nullptr) { ggml_free(graph_ctx); ctx.kv_self.ckpt.release_dsv4_per_step(); return false; } ggml_backend_buffer_set_usage(buffer, GGML_BACKEND_BUFFER_USAGE_COMPUTE); ggml_backend_buffer_clear(buffer, 0); ckpt.dsv4_per_step_shadow_ctxs.push_back(graph_ctx); ckpt.dsv4_per_step_shadow_bufs.push_back(buffer); } ckpt.dsv4_per_step_max_tokens = max_tokens; ckpt.dsv4_per_step_allocated = true; LLAMA_LOG_INFO("%s: DSV4 per-step base=%8.2f MiB delta=%8.2f MiB max_tokens=%d\n", __func__, ckpt.dsv4_per_step_base_bytes / (1024.0 * 1024.0), ckpt.dsv4_per_step_delta_bytes / (1024.0 * 1024.0), max_tokens); return true; } static bool dsv4_per_step_copy_base(llama_context & ctx, bool restore) { auto & ckpt = ctx.kv_self.ckpt; if (!ckpt.dsv4_per_step_allocated || ckpt.dsv4_per_step_state.size() != ckpt.dsv4_per_step_state_shadow.size()) { return false; } std::vector backends; for (size_t i = 0; i < ckpt.dsv4_per_step_state.size(); ++i) { ggml_tensor * state = ckpt.dsv4_per_step_state[i]; ggml_tensor * shadow = ckpt.dsv4_per_step_state_shadow[i]; ggml_backend_t backend = state != nullptr ? ggml_backend_sched_get_tensor_backend(ctx.sched, state) : nullptr; if (state == nullptr || shadow == nullptr) { return false; } if (backend == nullptr) { if (state->buffer == nullptr || shadow->buffer == nullptr) { return false; } ggml_backend_tensor_copy(restore ? shadow : state, restore ? state : shadow); continue; } if (restore) { ggml_backend_tensor_copy_async(backend, backend, shadow, state); } else { ggml_backend_tensor_copy_async(backend, backend, state, shadow); } if (std::find(backends.begin(), backends.end(), backend) == backends.end()) { backends.push_back(backend); } } for (ggml_backend_t backend : backends) { ggml_backend_synchronize(backend); } return true; } static bool dsv4_per_step_capture_group( llama_context & ctx, const std::vector & states, const llama_context::dsv4_runtime::comp_plan & plan) { auto & ckpt = ctx.kv_self.ckpt; if (plan.state_delta_src_idxs.size() != plan.state_delta_dst_idxs.size() || plan.state_delta_src_idxs.size() > (size_t) ckpt.dsv4_per_step_max_tokens) { return false; } for (ggml_tensor * state : states) { if (state == nullptr) { continue; } ggml_tensor * delta = llama_dsv4_spec_ckpt_delta(&ctx, state); ggml_backend_t backend = ggml_backend_sched_get_tensor_backend(ctx.sched, state); if (delta == nullptr || backend == nullptr || delta->ne[0] != state->ne[0]) { return false; } for (size_t row = 0; row < plan.state_delta_src_idxs.size(); ++row) { const int32_t src_idx = plan.state_delta_src_idxs[row]; const int32_t dst_idx = plan.state_delta_dst_idxs[row]; if (src_idx < 0 || (uint64_t) src_idx >= (uint64_t) delta->ne[1] || dst_idx < 0 || (uint64_t) dst_idx >= (uint64_t) state->ne[1]) { return false; } ggml_tensor src_view = *state; ggml_tensor dst_view = *delta; src_view.ne[1] = src_view.ne[2] = src_view.ne[3] = 1; dst_view.ne[1] = dst_view.ne[2] = dst_view.ne[3] = 1; src_view.nb[2] = src_view.nb[3] = src_view.nb[1]; dst_view.nb[2] = dst_view.nb[3] = dst_view.nb[1]; src_view.data = (char *) state->data + (size_t) dst_idx * state->nb[1]; dst_view.data = (char *) delta->data + (size_t) src_idx * delta->nb[1]; src_view.view_src = nullptr; dst_view.view_src = nullptr; src_view.view_offs = 0; dst_view.view_offs = 0; ggml_backend_tensor_copy_async(backend, backend, &src_view, &dst_view); } } return true; } bool llama_dsv4_spec_ckpt_capture_rows(llama_context * ctx) { if (ctx == nullptr || ctx->model.arch != LLM_ARCH_DEEPSEEK4) { return true; } const auto & ckpt = ctx->kv_self.ckpt; if (ckpt.selected_spec_mode != LLAMA_SPEC_CKPT_PER_STEP || !ckpt.dsv4_per_step_allocated || !ckpt.dsv4_per_step_saved) { return true; } const bool ok = dsv4_per_step_capture_group(*ctx, ctx->dsv4.cache.csa_state_kv, ctx->dsv4.csa_plan) && dsv4_per_step_capture_group(*ctx, ctx->dsv4.cache.csa_state_score, ctx->dsv4.csa_plan) && dsv4_per_step_capture_group(*ctx, ctx->dsv4.cache.hca_state_kv, ctx->dsv4.hca_plan) && dsv4_per_step_capture_group(*ctx, ctx->dsv4.cache.hca_state_score, ctx->dsv4.hca_plan) && dsv4_per_step_capture_group(*ctx, ctx->dsv4.cache.lid_state_kv, ctx->dsv4.lid_plan) && dsv4_per_step_capture_group(*ctx, ctx->dsv4.cache.lid_state_score, ctx->dsv4.lid_plan); if (!ok) { LLAMA_LOG_ERROR("%s: failed to queue DSV4 per-step compressor-state row capture\n", __func__); } return ok; } static bool dsv4_spec_ckpt_alloc_gpu( llama_context & ctx, const std::vector & tensors) { auto & ckpt = ctx.kv_self.ckpt; if (ckpt.dsv4_shadow_allocated) { return ckpt.dsv4_state_shadow.size() == tensors.size(); } struct tensor_entry { size_t index; ggml_tensor * source; }; std::map> entries_by_buft; const auto release_partial = [&]() { ckpt.release_dsv4_snapshot(); }; for (size_t i = 0; i < tensors.size(); ++i) { ggml_tensor * tensor = tensors[i]; if (tensor == nullptr) { continue; } if (tensor->buffer == nullptr) { return false; } entries_by_buft[ggml_backend_buffer_get_type(tensor->buffer)].push_back({ i, tensor }); } ckpt.dsv4_state_shadow.assign(tensors.size(), nullptr); for (auto & [buft, entries] : entries_by_buft) { ggml_init_params params = { /*.mem_size =*/ entries.size() * ggml_tensor_overhead(), /*.mem_buffer =*/ nullptr, /*.no_alloc =*/ true, }; ggml_context * shadow_ctx = ggml_init(params); if (shadow_ctx == nullptr) { release_partial(); return false; } for (const auto & entry : entries) { ggml_tensor * shadow = ggml_dup_tensor(shadow_ctx, entry.source); for (int d = 0; d < GGML_MAX_DIMS; ++d) { shadow->nb[d] = entry.source->nb[d]; } ggml_format_name(shadow, "dsv4_spec_shadow_%zu", entry.index); ckpt.dsv4_state_shadow[entry.index] = shadow; } ggml_backend_buffer_t buffer = ggml_backend_alloc_ctx_tensors_from_buft(shadow_ctx, buft); if (buffer == nullptr) { ggml_free(shadow_ctx); release_partial(); return false; } ggml_backend_buffer_clear(buffer, 0); LLAMA_LOG_INFO("%s: %10s DSV4 speculative shadow buffer = %8.2f MiB\n", __func__, ggml_backend_buffer_name(buffer), ggml_backend_buffer_get_size(buffer) / 1024.0 / 1024.0); ckpt.dsv4_shadow_ctxs.push_back(shadow_ctx); ckpt.dsv4_shadow_bufs.push_back(buffer); } ckpt.dsv4_shadow_allocated = true; return true; } static bool dsv4_spec_ckpt_copy_gpu( llama_context & ctx, const std::vector & tensors, bool restore) { auto & ckpt = ctx.kv_self.ckpt; if (!ckpt.dsv4_shadow_allocated || ckpt.dsv4_state_shadow.size() != tensors.size()) { return false; } for (size_t i = 0; i < tensors.size(); ++i) { ggml_tensor * tensor = tensors[i]; ggml_tensor * shadow = ckpt.dsv4_state_shadow[i]; if (tensor == nullptr || shadow == nullptr) { continue; } ggml_backend_t backend = ggml_backend_sched_get_tensor_backend(ctx.sched, tensor); if (backend == nullptr) { return false; } if (restore) { ggml_backend_tensor_copy_async(backend, backend, shadow, tensor); } else { ggml_backend_tensor_copy_async(backend, backend, tensor, shadow); } } return true; } bool llama_dsv4_spec_ckpt_prepare(llama_context * ctx, int mode, int max_tokens) { if (ctx == nullptr || ctx->model.arch != LLM_ARCH_DEEPSEEK4) { return true; } if (mode == LLAMA_SPEC_CKPT_PER_STEP) { return dsv4_per_step_alloc(*ctx, max_tokens); } if (mode == LLAMA_SPEC_CKPT_GPU_FALLBACK) { return dsv4_spec_ckpt_alloc_gpu(*ctx, dsv4_state_tensors(*ctx)); } return true; } bool llama_dsv4_spec_ckpt_save(llama_context * ctx, bool use_gpu) { if (ctx == nullptr || ctx->model.arch != LLM_ARCH_DEEPSEEK4) { return true; } if (ctx->kv_self.ckpt.selected_spec_mode == LLAMA_SPEC_CKPT_PER_STEP) { auto & ckpt = ctx->kv_self.ckpt; ckpt.dsv4_per_step_saved = false; ckpt.dsv4_per_step_csa_src.clear(); ckpt.dsv4_per_step_csa_dst.clear(); ckpt.dsv4_per_step_hca_src.clear(); ckpt.dsv4_per_step_hca_dst.clear(); ckpt.dsv4_per_step_lid_src.clear(); ckpt.dsv4_per_step_lid_dst.clear(); if (!use_gpu || !dsv4_per_step_copy_base(*ctx, false)) { LLAMA_LOG_ERROR("%s: failed to save DSV4 per-step compressor-state base\n", __func__); return false; } ckpt.dsv4_per_step_saved = true; return true; } const auto tensors = dsv4_state_tensors(*ctx); ctx->kv_self.ckpt.dsv4_shadow_saved = false; if (use_gpu) { if (!dsv4_spec_ckpt_alloc_gpu(*ctx, tensors) || !dsv4_spec_ckpt_copy_gpu(*ctx, tensors, false)) { LLAMA_LOG_ERROR("%s: failed to save DSV4 gpu-fallback checkpoint; explicit GPU mode will not downgrade to CPU\n", __func__); return false; } ctx->kv_self.ckpt.dsv4_state_data.clear(); ctx->kv_self.ckpt.dsv4_shadow_saved = true; return true; } auto & saved = ctx->kv_self.ckpt.dsv4_state_data; saved.clear(); for (ggml_tensor * tensor : tensors) { if (tensor == nullptr) { saved.emplace_back(); continue; } const size_t nbytes = ggml_nbytes(tensor); saved.emplace_back(nbytes); ggml_backend_tensor_get(tensor, saved.back().data(), 0, nbytes); } return true; } static enum llama_spec_ckpt_restore_result dsv4_per_step_restore_rows( llama_context & ctx, const std::vector & states, size_t delta_offset, const std::vector & deltas, const std::vector & src_idxs, const std::vector & dst_idxs, int accepted_step, std::vector & backends) { auto & ckpt = ctx.kv_self.ckpt; if (src_idxs.size() != dst_idxs.size() || src_idxs.size() > (size_t) ckpt.dsv4_per_step_max_tokens || delta_offset > deltas.size() || states.size() > deltas.size() - delta_offset) { LLAMA_LOG_ERROR("%s: invalid DSV4 per-step row restore: states=%zu delta_offset=%zu deltas=%zu src=%zu dst=%zu max=%d\n", __func__, states.size(), delta_offset, deltas.size(), src_idxs.size(), dst_idxs.size(), ckpt.dsv4_per_step_max_tokens); return LLAMA_SPEC_CKPT_RESTORE_FAILED; } for (size_t i = 0; i < states.size(); ++i) { ggml_tensor * state = states[i]; ggml_tensor * delta = deltas[delta_offset + i]; if (state == nullptr || delta == nullptr) { return LLAMA_SPEC_CKPT_RESTORE_FAILED; } ggml_backend_t backend = ggml_backend_sched_get_tensor_backend(ctx.sched, state); if (backend == nullptr && (state->buffer == nullptr || delta->buffer == nullptr)) { return LLAMA_SPEC_CKPT_RESTORE_FAILED; } for (size_t row = 0; row < src_idxs.size(); ++row) { if (src_idxs[row] > accepted_step) { continue; } // Reject invalid mappings instead of leaving stale compressor state. if (src_idxs[row] < 0 || dst_idxs[row] < 0 || (uint64_t) dst_idxs[row] >= (uint64_t) state->ne[1]) { LLAMA_LOG_ERROR("%s: invalid visible DSV4 state row src=%d dst=%d accepted_step=%d state_rows=%lld\n", __func__, src_idxs[row], dst_idxs[row], accepted_step, (long long) state->ne[1]); return LLAMA_SPEC_CKPT_RESTORE_FAILED; } ggml_tensor src_view = *delta; ggml_tensor dst_view = *state; src_view.ne[1] = src_view.ne[2] = src_view.ne[3] = 1; dst_view.ne[1] = dst_view.ne[2] = dst_view.ne[3] = 1; src_view.nb[2] = src_view.nb[3] = src_view.nb[1]; dst_view.nb[2] = dst_view.nb[3] = dst_view.nb[1]; src_view.data = (char *) delta->data + (size_t) src_idxs[row] * delta->nb[1]; dst_view.data = (char *) state->data + (size_t) dst_idxs[row] * state->nb[1]; src_view.view_src = nullptr; dst_view.view_src = nullptr; src_view.view_offs = 0; dst_view.view_offs = 0; if (backend != nullptr) { ggml_backend_tensor_copy_async(backend, backend, &src_view, &dst_view); } else { ggml_backend_tensor_copy(&src_view, &dst_view); } } if (backend != nullptr && std::find(backends.begin(), backends.end(), backend) == backends.end()) { backends.push_back(backend); } } return LLAMA_SPEC_CKPT_RESTORE_DIRECT; } enum llama_spec_ckpt_restore_result llama_dsv4_spec_ckpt_restore(llama_context * ctx, bool use_gpu, int accepted_step) { if (ctx == nullptr || ctx->model.arch != LLM_ARCH_DEEPSEEK4) { return LLAMA_SPEC_CKPT_RESTORE_FAILED; } auto & ckpt = ctx->kv_self.ckpt; if (ckpt.selected_spec_mode == LLAMA_SPEC_CKPT_PER_STEP) { if (!ckpt.dsv4_per_step_saved || !dsv4_per_step_copy_base(*ctx, true)) { LLAMA_LOG_ERROR("%s: failed to restore DSV4 per-step compressor-state base\n", __func__); return LLAMA_SPEC_CKPT_RESTORE_FAILED; } const auto compact = [](const std::vector & source) { std::vector result; for (ggml_tensor * tensor : source) { if (tensor != nullptr) { result.push_back(tensor); } } return result; }; const auto csa_kv = compact(ctx->dsv4.cache.csa_state_kv); const auto csa_score = compact(ctx->dsv4.cache.csa_state_score); const auto hca_kv = compact(ctx->dsv4.cache.hca_state_kv); const auto hca_score = compact(ctx->dsv4.cache.hca_state_score); const auto lid_kv = compact(ctx->dsv4.cache.lid_state_kv); const auto lid_score = compact(ctx->dsv4.cache.lid_state_score); const size_t csa_kv_off = 0; const size_t csa_score_off = csa_kv_off + csa_kv.size(); const size_t hca_kv_off = csa_score_off + csa_score.size(); const size_t hca_score_off = hca_kv_off + hca_kv.size(); const size_t lid_kv_off = hca_score_off + hca_score.size(); const size_t lid_score_off = lid_kv_off + lid_kv.size(); if (ckpt.dsv4_per_step_delta.size() != lid_score_off + lid_score.size()) { LLAMA_LOG_ERROR("%s: DSV4 per-step delta tensor layout mismatch\n", __func__); return LLAMA_SPEC_CKPT_RESTORE_FAILED; } std::vector backends; const auto restore_group = [&](const std::vector & states, size_t offset) { return dsv4_per_step_restore_rows(*ctx, states, offset, ckpt.dsv4_per_step_delta, offset == csa_kv_off || offset == csa_score_off ? ckpt.dsv4_per_step_csa_src : offset == hca_kv_off || offset == hca_score_off ? ckpt.dsv4_per_step_hca_src : ckpt.dsv4_per_step_lid_src, offset == csa_kv_off || offset == csa_score_off ? ckpt.dsv4_per_step_csa_dst : offset == hca_kv_off || offset == hca_score_off ? ckpt.dsv4_per_step_hca_dst : ckpt.dsv4_per_step_lid_dst, accepted_step, backends); }; if (restore_group(csa_kv, csa_kv_off) == LLAMA_SPEC_CKPT_RESTORE_FAILED || restore_group(csa_score, csa_score_off) == LLAMA_SPEC_CKPT_RESTORE_FAILED || restore_group(hca_kv, hca_kv_off) == LLAMA_SPEC_CKPT_RESTORE_FAILED || restore_group(hca_score, hca_score_off) == LLAMA_SPEC_CKPT_RESTORE_FAILED || restore_group(lid_kv, lid_kv_off) == LLAMA_SPEC_CKPT_RESTORE_FAILED || restore_group(lid_score, lid_score_off) == LLAMA_SPEC_CKPT_RESTORE_FAILED) { return LLAMA_SPEC_CKPT_RESTORE_FAILED; } for (ggml_backend_t backend : backends) { ggml_backend_synchronize(backend); } return LLAMA_SPEC_CKPT_RESTORE_DIRECT; } const auto tensors = dsv4_state_tensors(*ctx); if (use_gpu && ctx->kv_self.ckpt.dsv4_shadow_saved) { return dsv4_spec_ckpt_copy_gpu(*ctx, tensors, true) ? LLAMA_SPEC_CKPT_RESTORE_BASE_REPLAY_REQUIRED : LLAMA_SPEC_CKPT_RESTORE_FAILED; } const auto & saved = ctx->kv_self.ckpt.dsv4_state_data; if (saved.size() != tensors.size()) { LLAMA_LOG_ERROR("%s: DSV4 checkpoint tensor count mismatch: saved=%zu current=%zu\n", __func__, saved.size(), tensors.size()); return LLAMA_SPEC_CKPT_RESTORE_FAILED; } for (size_t i = 0; i < tensors.size(); ++i) { ggml_tensor * tensor = tensors[i]; if (tensor == nullptr) { if (!saved[i].empty()) { LLAMA_LOG_ERROR("%s: DSV4 checkpoint null tensor %zu has saved data\n", __func__, i); return LLAMA_SPEC_CKPT_RESTORE_FAILED; } continue; } if (saved[i].size() != ggml_nbytes(tensor)) { LLAMA_LOG_ERROR("%s: DSV4 checkpoint tensor %zu size mismatch\n", __func__, i); return LLAMA_SPEC_CKPT_RESTORE_FAILED; } if (!saved[i].empty()) { ggml_backend_tensor_set(tensor, saved[i].data(), 0, saved[i].size()); } } return LLAMA_SPEC_CKPT_RESTORE_BASE_REPLAY_REQUIRED; } ggml_tensor * llama_dsv4_spec_ckpt_delta(llama_context * ctx, ggml_tensor * state_tensor) { if (ctx == nullptr || state_tensor == nullptr || ctx->kv_self.ckpt.selected_spec_mode != LLAMA_SPEC_CKPT_PER_STEP || !ctx->kv_self.ckpt.dsv4_per_step_allocated) { return nullptr; } auto & ckpt = ctx->kv_self.ckpt; for (size_t i = 0; i < ckpt.dsv4_per_step_state.size(); ++i) { if (ckpt.dsv4_per_step_state[i] == state_tensor) { return ckpt.dsv4_per_step_delta[i]; } } return nullptr; } void llama_dsv4_spec_ckpt_record_plan(llama_context * ctx) { if (ctx == nullptr || ctx->kv_self.ckpt.selected_spec_mode != LLAMA_SPEC_CKPT_PER_STEP) { return; } auto & ckpt = ctx->kv_self.ckpt; ckpt.dsv4_per_step_csa_src = ctx->dsv4.csa_plan.state_delta_src_idxs; ckpt.dsv4_per_step_csa_dst = ctx->dsv4.csa_plan.state_delta_dst_idxs; ckpt.dsv4_per_step_hca_src = ctx->dsv4.hca_plan.state_delta_src_idxs; ckpt.dsv4_per_step_hca_dst = ctx->dsv4.hca_plan.state_delta_dst_idxs; ckpt.dsv4_per_step_lid_src = ctx->dsv4.lid_plan.state_delta_src_idxs; ckpt.dsv4_per_step_lid_dst = ctx->dsv4.lid_plan.state_delta_dst_idxs; } void llama_dsv4_spec_ckpt_discard(llama_context * ctx) { if (ctx != nullptr) { ctx->kv_self.ckpt.dsv4_state_data.clear(); ctx->kv_self.ckpt.dsv4_shadow_saved = false; ctx->kv_self.ckpt.dsv4_per_step_saved = false; ctx->kv_self.ckpt.dsv4_per_step_csa_src.clear(); ctx->kv_self.ckpt.dsv4_per_step_csa_dst.clear(); ctx->kv_self.ckpt.dsv4_per_step_hca_src.clear(); ctx->kv_self.ckpt.dsv4_per_step_hca_dst.clear(); ctx->kv_self.ckpt.dsv4_per_step_lid_src.clear(); ctx->kv_self.ckpt.dsv4_per_step_lid_dst.clear(); } } bool llama_prepare_dsv4_graph_inputs(llama_context & lctx, const llama_batch & batch, bool set_tensors, bool reserve_plan) { if (lctx.model.arch != LLM_ARCH_DEEPSEEK4) { return true; } if (!dsv4_validate_batch_seq_ids(lctx, batch)) { return false; } // Standalone companions contain only the predictor block, skip target state planning. const bool is_dsv4_mtp = lctx.model.mtp && lctx.cparams.mtp_op_type != MTP_OP_NONE && lctx.model.hparams.nextn_predict_layers > 0 && lctx.model.hparams.dsv4_compress_ratios[(size_t) (lctx.model.hparams.n_layer - lctx.model.hparams.nextn_predict_layers)] == 0; if (is_dsv4_mtp) { lctx.dsv4.raw = {}; if (!reserve_plan && !dsv4_build_raw_context(lctx, batch, lctx.dsv4.raw)) { return false; } lctx.dsv4.csa_plan = {}; lctx.dsv4.hca_plan = {}; lctx.dsv4.lid_plan = {}; lctx.dsv4.csa_ctx = {}; lctx.dsv4.hca_ctx = {}; lctx.dsv4.lid_ctx = {}; if (set_tensors) { dsv4_set_input_tensor(lctx.dsv4.inputs.raw_k_write_src_idxs, lctx.dsv4.raw.write_src_idxs); dsv4_set_input_tensor(lctx.dsv4.inputs.raw_k_write_idxs, lctx.dsv4.raw.write_dst_idxs); dsv4_set_input_tensor(lctx.dsv4.inputs.raw_k_read_idxs, lctx.dsv4.raw.read_dst_idxs); } return true; } if (!lctx.ensure_dsv4_cache_tensors()) { return false; } const uint32_t cache_n_stream = std::max(1, lctx.dsv4.cache.n_stream); const uint32_t csa_kv_size = dsv4_cache_kv_size(lctx.dsv4.cache.csa_k)/cache_n_stream; const uint32_t hca_kv_size = dsv4_cache_kv_size(lctx.dsv4.cache.hca_k)/cache_n_stream; const uint32_t lid_kv_size = dsv4_cache_kv_size(lctx.dsv4.cache.lid_k)/cache_n_stream; const uint32_t csa_state_size = dsv4_cache_state_size(lctx.dsv4.cache.csa_state_kv)/cache_n_stream; const uint32_t hca_state_size = dsv4_cache_state_size(lctx.dsv4.cache.hca_state_kv)/cache_n_stream; const uint32_t lid_state_size = dsv4_cache_state_size(lctx.dsv4.cache.lid_state_kv)/cache_n_stream; const auto build_plan = [&](uint32_t ratio, bool overlap, uint32_t state_size, uint32_t kv_size, uint32_t n_stream) { return reserve_plan ? dsv4_build_reserve_comp_plan(batch, ratio, overlap, state_size, kv_size, n_stream) : dsv4_build_comp_plan(batch, ratio, overlap, state_size, kv_size, n_stream); }; lctx.dsv4.raw = {}; if (!reserve_plan && !dsv4_build_raw_context(lctx, batch, lctx.dsv4.raw)) { return false; } //auto tim1 = ggml_time_us(); lctx.dsv4.csa_plan = build_plan(llama_context::dsv4_runtime::CSA_RATIO, true, csa_state_size, csa_kv_size, cache_n_stream); lctx.dsv4.hca_plan = build_plan(llama_context::dsv4_runtime::HCA_RATIO, false, hca_state_size, hca_kv_size, cache_n_stream); lctx.dsv4.lid_plan = build_plan(llama_context::dsv4_runtime::CSA_RATIO, true, lid_state_size, lid_kv_size, cache_n_stream); lctx.dsv4.csa_ctx = dsv4_build_comp_context(batch, cache_n_stream, lctx.dsv4.csa_plan.n_kv); lctx.dsv4.hca_ctx = dsv4_build_comp_context(batch, cache_n_stream, lctx.dsv4.hca_plan.n_kv); lctx.dsv4.lid_ctx = dsv4_build_comp_context(batch, cache_n_stream, lctx.dsv4.lid_plan.n_kv); //auto tim2 = ggml_time_us(); //fprintf(stderr, "%s: %ld us to buils plans\n", __func__, tim2-tim1); if (!dsv4_validate_comp_plan("csa", batch, lctx.dsv4.csa_plan, llama_context::dsv4_runtime::CSA_RATIO, true, csa_state_size, csa_kv_size, cache_n_stream) || !dsv4_validate_comp_plan("hca", batch, lctx.dsv4.hca_plan, llama_context::dsv4_runtime::HCA_RATIO, false, hca_state_size, hca_kv_size, cache_n_stream) || !dsv4_validate_comp_plan("lid", batch, lctx.dsv4.lid_plan, llama_context::dsv4_runtime::CSA_RATIO, true, lid_state_size, lid_kv_size, cache_n_stream) || !dsv4_validate_csa_lid_visibility(lctx, csa_kv_size, lid_kv_size)) { return false; } if (!set_tensors) { return true; } //tim1 = ggml_time_us(); dsv4_set_input_tensor(lctx.dsv4.inputs.raw_k_write_src_idxs, lctx.dsv4.raw.write_src_idxs); dsv4_set_input_tensor(lctx.dsv4.inputs.raw_k_write_idxs, lctx.dsv4.raw.write_dst_idxs); dsv4_set_input_tensor(lctx.dsv4.inputs.raw_k_read_idxs, lctx.dsv4.raw.read_dst_idxs); auto set_comp = [&](llama_context::dsv4_runtime::comp_inputs & inputs, llama_context::dsv4_runtime::comp_plan & plan, bool set_mask) { dsv4_set_input_tensor(inputs.state_pos, plan.state_pos); dsv4_set_input_tensor(inputs.state_persist_src_idxs, plan.state_persist_src_idxs); dsv4_set_input_tensor(inputs.state_persist_dst_idxs, plan.state_persist_dst_idxs); dsv4_set_input_tensor(inputs.state_read_idxs, plan.state_read_idxs); dsv4_set_input_tensor(inputs.state_write_idxs, plan.state_write_idxs); dsv4_set_input_tensor(inputs.state_write_pos, plan.state_write_pos); if (set_mask) { dsv4_set_mask_tensor(inputs.kq_mask, plan, batch.n_tokens); } }; set_comp(lctx.dsv4.inputs.csa, lctx.dsv4.csa_plan, true); set_comp(lctx.dsv4.inputs.hca, lctx.dsv4.hca_plan, true); set_comp(lctx.dsv4.inputs.lid, lctx.dsv4.lid_plan, false); llama_dsv4_spec_ckpt_record_plan(&lctx); //tim2 = ggml_time_us(); //fprintf(stderr, "%s: setting input tensors took %ld us\n", __func__, tim2 - tim1); return true; }