#include "llama-memory-hybrid-idx.h"
#include <algorithm>
#include <cmath>
#include <type_traits>
#include "llama-impl.h"
#include "llama-batch.h"
#include "llama-io.h"
#include "llama-model.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iterator>
#include <stdexcept>
llama_memory_hybrid_idx::llama_memory_hybrid_idx(
const llama_model & model,
ggml_type type_k,
ggml_type type_v,
bool v_trans,
uint32_t kv_size,
uint32_t n_pad,
uint32_t n_swa,
llama_swa_type swa_type,
ggml_type type_r,
ggml_type type_s,
uint32_t rs_size,
uint32_t n_seq_max,
uint32_t n_rs_seq,
bool offload,
bool unified,
const layer_filter_cb & filter_attn,
const layer_filter_cb & filter_recr,
const layer_filter_cb & filter_idx) :
llama_memory_hybrid(
model,
type_k, type_v, v_trans, kv_size, n_pad, n_swa, swa_type,
type_r, type_s, rs_size,
n_seq_max, n_rs_seq, offload, unified,
filter_attn, filter_recr),
hparams_idx(model.hparams),
mem_idx(filter_idx == nullptr ? nullptr : [&] {
std::fill(hparams_idx.n_head_kv_arr.begin(), hparams_idx.n_head_kv_arr.end(), 1);
hparams_idx.n_embd_head_k_full = model.hparams.indexer_head_size * (model.hparams.indexer_kpool > 0 ? model.hparams.indexer_kpool_row : 1);
hparams_idx.rope_type = LLAMA_ROPE_TYPE_NONE;
hparams_idx.n_embd_head_k_mla_impl = model.hparams.indexer_head_size;
hparams_idx.n_embd_head_v_mla_impl = model.hparams.indexer_head_size;
LLAMA_LOG_INFO("%s: creating indexer KV cache, size = %u cells\n", __func__, kv_size);
return new llama_kv_cache(
model, hparams_idx, type_k, type_v, v_trans, offload, unified,
kv_size, n_seq_max, n_pad, n_swa, swa_type,
nullptr, filter_idx, nullptr, nullptr, "idx_");
}()) {}
llama_memory_context_ptr llama_memory_hybrid_idx::init_batch(llama_batch_allocr & balloc, uint32_t n_ubatch, bool embd_all) {
do {
balloc.split_reset();
std::vector<llama_ubatch> ubatches;
while (true) {
llama_ubatch ubatch;
if (embd_all) {
ubatch = balloc.split_seq(n_ubatch);
} else {
const bool unified = (get_mem_attn()->get_n_stream() == 1);
const uint32_t n_rs_seq = get_mem_recr()->n_rs_seq;
ubatch = balloc.split_equal(n_ubatch, !unified, n_rs_seq > 0 ? n_rs_seq + 1 : 0);
}
if (ubatch.n_tokens == 0) {
break;
}
ubatches.push_back(std::move(ubatch)); }
if (balloc.get_n_used() < balloc.get_n_tokens()) {
break;
}
if (!get_mem_recr()->prepare(ubatches)) {
LLAMA_LOG_ERROR("%s: failed to prepare recurrent ubatches\n", __func__);
return std::make_unique<llama_memory_hybrid_idx_context>(LLAMA_MEMORY_STATUS_FAILED_PREPARE);
}
auto heads_attn = get_mem_attn()->prepare(ubatches);
if (heads_attn.empty()) {
LLAMA_LOG_ERROR("%s: failed to prepare attention ubatches\n", __func__);
return std::make_unique<llama_memory_hybrid_idx_context>(LLAMA_MEMORY_STATUS_FAILED_PREPARE);
}
llama_kv_cache::slot_info_vec_t heads_idx;
if (mem_idx) {
heads_idx = heads_attn;
}
return std::make_unique<llama_memory_hybrid_idx_context>(
this, std::move(heads_attn), std::move(heads_idx), std::move(ubatches));
} while(false);
return std::make_unique<llama_memory_hybrid_idx_context>(LLAMA_MEMORY_STATUS_FAILED_PREPARE);
}
llama_memory_context_ptr llama_memory_hybrid_idx::init_full() {
return std::make_unique<llama_memory_hybrid_idx_context>(this);
}
llama_memory_context_ptr llama_memory_hybrid_idx::init_update(llama_context * lctx, bool optimize) {
return std::make_unique<llama_memory_hybrid_idx_context>(this, lctx, optimize);
}
void llama_memory_hybrid_idx::clear(bool data) {
llama_memory_hybrid::clear(data);
if (mem_idx) {
mem_idx->clear(data);
mem_idx_stale_set(-1, 0);
}
}
void llama_memory_hybrid_idx::mem_idx_stale_set(llama_seq_id seq_id, llama_pos p0) {
p0 = std::max<llama_pos>(p0, 0);
if (seq_id < 0) {
for (auto & p : mem_idx_stale) {
p = std::min(p, p0);
}
return;
}
GGML_ASSERT(seq_id < (llama_seq_id) LLAMA_MAX_SEQ);
mem_idx_stale[seq_id] = std::min(mem_idx_stale[seq_id], p0);
}
llama_pos llama_memory_hybrid_idx::mem_idx_stale_pos(llama_seq_id seq_id, llama_pos p0) const {
if (seq_id < 0 || p0 <= mem_idx->seq_pos_min(seq_id)) {
return 0;
}
return p0;
}
bool llama_memory_hybrid_idx::seq_rm(llama_seq_id seq_id, llama_pos p0, llama_pos p1) {
if (!get_mem_recr()->seq_rm(seq_id, p0, p1)) {
return false;
}
if (mem_idx) {
const llama_pos stale = mem_idx_stale_pos(seq_id, p0);
mem_idx->seq_rm(seq_id, p0, p1);
mem_idx_stale_set(seq_id, stale);
}
return get_mem_attn()->seq_rm(seq_id, p0, p1);
}
void llama_memory_hybrid_idx::seq_cp(llama_seq_id seq_id_src, llama_seq_id seq_id_dst, llama_pos p0, llama_pos p1) {
GGML_ASSERT(p0 <= 0 && p1 < 0 && "partial seq_cp is not supported");
llama_memory_hybrid::seq_cp(seq_id_src, seq_id_dst, p0, p1);
if (mem_idx) {
mem_idx->seq_cp(seq_id_src, seq_id_dst, p0, p1);
mem_idx_stale_set(seq_id_dst, 0);
}
}
void llama_memory_hybrid_idx::seq_keep(llama_seq_id seq_id) {
llama_memory_hybrid::seq_keep(seq_id);
if (mem_idx) {
mem_idx->seq_keep(seq_id);
mem_idx_stale_set(-1, 0);
}
}
void llama_memory_hybrid_idx::seq_add(llama_seq_id seq_id, llama_pos p0, llama_pos p1, llama_pos shift) {
llama_memory_hybrid::seq_add(seq_id, p0, p1, shift);
if (mem_idx) {
const llama_pos stale = mem_idx_stale_pos(seq_id, shift < 0 ? p0 + shift : p0);
mem_idx->seq_add(seq_id, p0, p1, shift);
mem_idx_stale_set(seq_id, stale);
}
}
void llama_memory_hybrid_idx::seq_div(llama_seq_id seq_id, llama_pos p0, llama_pos p1, int d) {
llama_memory_hybrid::seq_div(seq_id, p0, p1, d);
if (mem_idx) {
mem_idx->seq_div(seq_id, p0, p1, d);
mem_idx_stale_set(seq_id, 0);
}
}
std::map<ggml_backend_buffer_type_t, size_t> llama_memory_hybrid_idx::memory_breakdown() const {
std::map<ggml_backend_buffer_type_t, size_t> mb = llama_memory_hybrid::memory_breakdown();
if (mem_idx) {
for (const auto & buft_size : mem_idx->memory_breakdown()) {
mb[buft_size.first] += buft_size.second;
}
}
return mb;
}
void llama_memory_hybrid_idx::state_write(llama_io_write_i & io, llama_seq_id seq_id, llama_state_seq_flags flags) const {
llama_memory_hybrid::state_write(io, seq_id, flags);
if ((flags & LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY) == 0) {
if (mem_idx) {
mem_idx->state_write(io, seq_id, flags);
}
}
}
void llama_memory_hybrid_idx::state_read(llama_io_read_i & io, llama_seq_id seq_id, llama_state_seq_flags flags) {
llama_kv_cache::slot_info_vec_t sinfos_attn;
try {
if ((flags & LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY) == 0) {
get_mem_attn()->state_read_sinfo(io, seq_id, flags, mem_idx ? &sinfos_attn : nullptr, nullptr);
}
get_mem_recr()->state_read(io, seq_id, flags);
if ((flags & LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY) == 0) {
if (mem_idx) {
mem_idx->state_read_sinfo(io, seq_id, flags, nullptr, &sinfos_attn);
mem_idx_stale_set(seq_id, 0);
}
}
} catch (...) {
state_drop(seq_id);
throw;
}
}
void llama_memory_hybrid_idx::state_drop(llama_seq_id seq_id) {
if (seq_id < 0) {
clear(true);
return;
}
get_mem_attn()->state_clear(seq_id);
get_mem_recr()->seq_rm(seq_id, -1, -1);
if (mem_idx) {
mem_idx->state_clear(seq_id);
mem_idx_stale_set(seq_id, 0);
}
}
llama_kv_cache * llama_memory_hybrid_idx::get_mem_idx() const {
return mem_idx.get();
}
static std::vector<uint32_t> llama_memory_hybrid_idx_ns(const llama_kv_cache::slot_info_vec_t & sinfos) {
std::vector<uint32_t> res;
res.reserve(sinfos.size());
for (const auto & sinfo : sinfos) {
res.push_back(sinfo.s1 - sinfo.s0 + 1);
}
return res;
}
struct llama_memory_hybrid_idx::kpool_layout {
struct seq {
llama_pos pos_min = 0;
uint32_t strm = 0; std::vector<std::pair<llama_pos, uint32_t>> cells; std::vector<uint32_t> pools;
size_t j_next = 0;
};
std::array<seq, LLAMA_MAX_SEQ> seqs;
uint32_t n_pool_real = 0;
};
struct llama_memory_hybrid_idx_context::kpool_state {
std::vector<uint32_t> is_new;
std::vector<uint32_t> rep_gen; uint32_t generation = 0;
uint32_t n_pool_real = 0;
uint32_t n_new = 0;
uint32_t n_new_g = 1; };
namespace {
uint32_t kpool_pad(uint32_t n_pool) {
return std::max<uint32_t>(64u, GGML_PAD(n_pool + 1, 64u));
}
int64_t kpool_rank(const std::vector<std::pair<llama_pos, uint32_t>> & cells, llama_pos pos, uint32_t cell) {
auto it = std::lower_bound(cells.begin(), cells.end(), std::make_pair(pos, cell));
return it != cells.end() && it->second == cell && it->first == pos ? it - cells.begin() : -1;
}
}
llama_memory_hybrid_idx::~llama_memory_hybrid_idx() = default;
const llama_memory_hybrid_idx::kpool_layout & llama_memory_hybrid_idx::kpool_layout_get() const {
GGML_ASSERT(kpool_lay != nullptr);
return *kpool_lay;
}
const llama_memory_hybrid_idx::kpool_layout & llama_memory_hybrid_idx::kpool_layout_update() {
GGML_ASSERT(mem_idx != nullptr);
if (!kpool_lay) {
kpool_lay = std::make_unique<kpool_layout>();
}
auto & lay = *kpool_lay;
const uint32_t kpool = get_kpool();
const uint32_t n_stream_kv = mem_idx->get_n_stream();
const bool unified = n_stream_kv == 1;
lay.n_pool_real = 0;
for (llama_seq_id s = 0; s < LLAMA_MAX_SEQ; ++s) {
auto & sq = lay.seqs[s];
if (!unified && s >= (llama_seq_id) n_stream_kv) {
sq = kpool_layout::seq();
continue;
}
const auto & cells = mem_idx->get_cells(unified ? 0 : s);
const auto & sp = cells.seq_pos_get(s);
sq.strm = unified ? 0 : mem_idx->get_stream(s);
if (mem_idx_stale[s] == POS_CLEAN && !sq.cells.empty() && !sp.empty() &&
sq.pos_min == sp.begin()->first) {
for (auto it = sp.upper_bound(sq.cells.back()); it != sp.end(); ++it) {
sq.cells.push_back(*it);
}
}
if (sq.cells.size() != sp.size() || mem_idx_stale[s] != POS_CLEAN) {
sq.cells.assign(sp.begin(), sp.end());
sq.pools.clear();
sq.j_next = 0;
sq.pos_min = sp.empty() ? 0 : sp.begin()->first;
}
size_t j = sq.j_next;
if (hparams_idx.indexer_kpool_by_order) {
for (; j + kpool <= sq.cells.size(); j += kpool) {
sq.pools.push_back((uint32_t) j);
}
} else {
while (j + kpool <= sq.cells.size()) {
const llama_pos p0 = sq.cells[j].first;
if ((p0 - sq.pos_min) % (llama_pos) kpool != 0) {
++j;
continue;
}
bool ok = true;
for (uint32_t k = 1; k < kpool; ++k) {
if (sq.cells[j + k].first != p0 + (llama_pos) k) {
ok = false;
break;
}
}
if (ok) {
sq.pools.push_back((uint32_t) j);
j += kpool;
} else {
++j;
}
}
}
sq.j_next = j;
lay.n_pool_real += (uint32_t) sq.pools.size();
}
return lay;
}
llama_memory_hybrid_idx_context::llama_memory_hybrid_idx_context(llama_memory_status status) :
llama_memory_hybrid_context(status) {}
llama_memory_hybrid_idx_context::llama_memory_hybrid_idx_context(llama_memory_hybrid_idx * mem) :
llama_memory_hybrid_context(mem),
mem(mem),
ns_ubatch(mem->get_mem_idx() == nullptr ?
std::vector<uint32_t>() : std::vector<uint32_t>{ mem->get_mem_idx()->get_n_stream() }),
ctx_idx(mem->get_mem_idx() == nullptr ? nullptr :
new llama_kv_cache_context(mem->get_mem_idx())) {
if (kpool_track()) {
mem->kpool_layout_update();
auto st = kpool_build_sizes();
const auto * idx = mem->get_mem_idx();
const uint64_t n_pool_max = uint64_t(idx->get_size() / mem->get_kpool()) * idx->get_n_seq_max();
GGML_ASSERT(n_pool_max <= UINT32_MAX - 64);
st.n_pool_real = std::max(st.n_pool_real, uint32_t(n_pool_max));
st.n_new = st.n_pool_real;
st.n_new_g = std::max(st.n_new, 1u);
kpool_st = std::make_unique<kpool_state>(std::move(st));
i_kpool = 0;
}
}
llama_memory_hybrid_idx_context::llama_memory_hybrid_idx_context(
llama_memory_hybrid_idx * mem,
llama_context * lctx,
bool optimize) :
llama_memory_hybrid_context(mem, lctx, optimize),
mem(mem),
ctx_idx(mem->get_mem_idx() == nullptr ? nullptr :
mem->get_mem_idx()->init_update(lctx, optimize)) {}
llama_memory_hybrid_idx_context::llama_memory_hybrid_idx_context(
llama_memory_hybrid_idx * mem,
slot_info_vec_t sinfos_attn,
slot_info_vec_t sinfos_idx,
std::vector<llama_ubatch> ubatches) :
llama_memory_hybrid_context(mem, std::move(sinfos_attn), ubatches),
mem(mem),
ns_ubatch(llama_memory_hybrid_idx_ns(sinfos_idx)),
sinfos_kpool(mem->get_mem_idx() != nullptr && mem->get_kpool() > 0 && mem->get_kpool_by_order() ? sinfos_idx : slot_info_vec_t()),
ctx_idx(mem->get_mem_idx() == nullptr ? nullptr :
new llama_kv_cache_context(mem->get_mem_idx(), std::move(sinfos_idx), ubatches)) {
mem_idx_stale_batch = mem->mem_idx_stale_get();
}
llama_memory_hybrid_idx_context::~llama_memory_hybrid_idx_context() = default;
bool llama_memory_hybrid_idx_context::next() {
if (i_cur == 0 && mem != nullptr) {
mem->mem_idx_stale_clear();
}
if (ctx_idx) {
ctx_idx->next();
}
++i_cur;
return llama_memory_hybrid_context::next();
}
bool llama_memory_hybrid_idx_context::apply() {
bool res = llama_memory_hybrid_context::apply();
if (ctx_idx) {
res = res & ctx_idx->apply();
}
if (res && kpool_track()) {
mem->kpool_layout_update();
if (!kpool_st) {
kpool_st = std::make_unique<kpool_state>();
}
kpool_build_state(get_ubatch());
i_kpool = i_cur;
}
return res;
}
bool llama_memory_hybrid_idx_context::kpool_track() const {
return mem != nullptr && mem->get_mem_idx() != nullptr && mem->get_kpool() > 0 && !ns_ubatch.empty();
}
const llama_kv_cache_context * llama_memory_hybrid_idx_context::get_idx() const {
return static_cast<const llama_kv_cache_context *>(ctx_idx.get());
}
uint32_t llama_memory_hybrid_idx_context::get_n_stream() const {
GGML_ASSERT(i_cur < ns_ubatch.size());
return ns_ubatch[i_cur];
}
llama_memory_hybrid_idx_context::kpool_access::kpool_access(ggml_context * ctx, ggml_tensor * k, int64_t n_embd) : ctx(ctx) {
const int64_t n_tok = k->ne[0] - n_embd;
GGML_ASSERT(n_tok > 0 && n_tok % n_embd == 0);
const int64_t n_cells = k->ne[1]*k->ne[2];
key_gate = ggml_view_2d(ctx, k, n_tok, n_cells, k->nb[1], 0);
pooled = ggml_view_2d(ctx, k, n_embd, n_cells, k->nb[1], ggml_row_size(k->type, n_tok));
}
ggml_tensor * llama_memory_hybrid_idx_context::kpool_access::gather_key_gate(ggml_tensor * idxs) const {
return ggml_get_rows(ctx, key_gate, idxs);
}
ggml_tensor * llama_memory_hybrid_idx_context::kpool_access::scatter_pooled(ggml_tensor * values, ggml_tensor * idxs) const {
return ggml_set_rows(ctx, pooled, values, idxs);
}
ggml_tensor * llama_memory_hybrid_idx_context::kpool_access::gather_pooled(ggml_tensor * idxs) const {
return ggml_get_rows(ctx, pooled, idxs);
}
llama_memory_hybrid_idx_context::kpool_access llama_memory_hybrid_idx_context::get_kpool_access(
ggml_context * ctx, int32_t il, int64_t n_embd) const {
GGML_ASSERT(mem != nullptr && mem->get_mem_idx() != nullptr);
return kpool_access(ctx, mem->get_mem_idx()->get_k_storage(il), n_embd);
}
llama_memory_hybrid_idx_context::kpool_state llama_memory_hybrid_idx_context::kpool_build_sizes() const {
const auto & lay = mem->kpool_layout_get();
kpool_state st;
st.n_pool_real = lay.n_pool_real;
return st;
}
void llama_memory_hybrid_idx_context::kpool_build_state(const llama_ubatch & ubatch) {
const auto & lay = mem->kpool_layout_get();
auto & st = *kpool_st;
const auto * idx = mem->get_mem_idx();
const uint32_t kv_size = idx->get_size();
const uint32_t kpool = mem->get_kpool();
st.n_pool_real = lay.n_pool_real;
st.n_new = 0;
if (++st.generation == 0) {
std::fill(st.is_new.begin(), st.is_new.end(), 0);
std::fill(st.rep_gen.begin(), st.rep_gen.end(), 0);
st.generation = 1;
}
st.is_new.resize(lay.n_pool_real, 0);
st.rep_gen.resize((size_t) kv_size*idx->get_n_stream(), 0);
std::array<uint32_t, LLAMA_MAX_SEQ> pool_start;
auto mark = [&](llama_seq_id s, size_t k) {
const auto & sq = lay.seqs[s];
const size_t rep = (size_t) sq.strm*kv_size + sq.cells[sq.pools[k] + kpool - 1].second;
if (st.rep_gen[rep] != st.generation) {
st.rep_gen[rep] = st.generation;
st.is_new[pool_start[s] + k] = st.generation;
++st.n_new;
}
};
uint32_t ip = 0;
for (llama_seq_id s = 0; s < LLAMA_MAX_SEQ; ++s) {
const auto & sq = lay.seqs[s];
pool_start[s] = ip;
ip += (uint32_t) sq.pools.size();
const llama_pos stale_from = i_cur == 0 ?
mem_idx_stale_batch[s] : llama_memory_hybrid_idx::POS_CLEAN;
if (stale_from == llama_memory_hybrid_idx::POS_CLEAN) {
continue;
}
auto first = std::lower_bound(sq.pools.begin(), sq.pools.end(), stale_from,
[&](uint32_t j, llama_pos p) { return sq.cells[j + kpool - 1].first < p; });
for (auto it = first; it != sq.pools.end(); ++it) {
mark(s, it - sq.pools.begin());
}
}
GGML_ASSERT(ip == st.is_new.size());
const bool by_order = mem->get_kpool_by_order();
const auto * sinfo = by_order ? &sinfos_kpool[i_cur] : nullptr;
const uint32_t n_tps = by_order ? (uint32_t) sinfo->size() : 0;
for (uint32_t i = 0; i < ubatch.n_tokens; ++i) {
const llama_pos p = ubatch.pos[i];
for (int32_t k = 0; k < ubatch.n_seq_id[i]; ++k) {
const llama_seq_id s = ubatch.seq_id[i][k];
const auto & sq = lay.seqs[s];
if (by_order) {
const int64_t r = kpool_rank(sq.cells, p, sinfo->idxs[i / n_tps][i % n_tps]);
GGML_ASSERT(r >= 0);
if ((size_t) r / kpool < sq.pools.size()) {
mark(s, (size_t) r / kpool);
}
continue;
}
auto it = std::upper_bound(sq.pools.begin(), sq.pools.end(), p,
[&](llama_pos pos, uint32_t j) { return pos < sq.cells[j].first; });
if (it == sq.pools.begin()) {
continue;
}
--it;
if (p <= sq.cells[*it + kpool - 1].first) {
mark(s, it - sq.pools.begin());
}
}
}
const uint32_t n_pool_max = kv_size / kpool * idx->get_n_seq_max();
const uint32_t bound = ubatch.n_tokens/kpool + ubatch.n_seqs_unq;
st.n_new_g = std::max({st.n_new, 1u, std::min({bound, kpool_pad(st.n_pool_real) - 1, n_pool_max})});
}
const llama_memory_hybrid_idx_context::kpool_state & llama_memory_hybrid_idx_context::kpool_cur() const {
GGML_ASSERT(kpool_st != nullptr && i_kpool == i_cur && "k-pool state read before apply()");
return *kpool_st;
}
uint32_t llama_memory_hybrid_idx_context::get_n_kpool() const {
return kpool_pad(kpool_cur().n_pool_real);
}
uint32_t llama_memory_hybrid_idx_context::get_n_kpool_new() const {
return kpool_cur().n_new_g;
}
void llama_memory_hybrid_idx_context::set_input_kpool(ggml_tensor * pool_cells, ggml_tensor * pool_idxs, ggml_tensor * pool_mask, ggml_tensor * tail_idxs,
ggml_tensor * sel_mask, ggml_tensor * new_pool_idxs, ggml_tensor * new_pool_rep,
const llama_ubatch * ubatch, ggml_tensor * new_pool_pos) const {
GGML_ASSERT(mem != nullptr && mem->get_mem_idx() != nullptr);
GGML_ASSERT(ggml_backend_buffer_is_host(pool_cells->buffer));
GGML_ASSERT(ggml_backend_buffer_is_host(pool_idxs->buffer));
GGML_ASSERT(ggml_backend_buffer_is_host(pool_mask->buffer));
GGML_ASSERT(ggml_backend_buffer_is_host(tail_idxs->buffer));
const uint32_t kpool = mem->get_kpool();
const uint32_t n_kv = get_idx()->get_n_kv();
const auto & st = kpool_cur();
const auto & lay = mem->kpool_layout_get();
const uint32_t n_tokens = ubatch->n_tokens;
const uint32_t n_pool = (uint32_t) pool_cells->ne[0];
const uint32_t n_new = st.n_new;
const uint32_t n_new_g = st.n_new_g;
const bool by_order = mem->get_kpool_by_order();
GGML_ASSERT(n_pool == kpool_pad(st.n_pool_real));
GGML_ASSERT(st.is_new.size() == st.n_pool_real);
GGML_ASSERT(pool_mask->ne[0] == (int64_t) n_pool && pool_mask->ne[1] == (int64_t) n_tokens);
GGML_ASSERT(tail_idxs->ne[0] == (int64_t) kpool - 1 && tail_idxs->ne[1] == (int64_t) n_tokens);
GGML_ASSERT(pool_idxs->ne[0] == (int64_t) kpool && pool_idxs->ne[1] == (int64_t) n_pool);
GGML_ASSERT(ggml_backend_buffer_is_host(new_pool_idxs->buffer));
GGML_ASSERT(new_pool_idxs->ne[0] == (int64_t) kpool && new_pool_idxs->ne[1] == (int64_t) n_new_g);
GGML_ASSERT(new_pool_rep != nullptr && ggml_backend_buffer_is_host(new_pool_rep->buffer));
GGML_ASSERT(new_pool_rep->ne[0] == (int64_t) n_new_g);
if (new_pool_pos != nullptr) {
GGML_ASSERT(ggml_backend_buffer_is_host(new_pool_pos->buffer));
GGML_ASSERT(new_pool_pos->ne[0] == 4*(int64_t) n_new_g);
}
const uint32_t kv_size = mem->get_mem_idx()->get_size();
const uint32_t n_stream_kv = mem->get_mem_idx()->get_n_stream();
auto gcell = [&](const llama_memory_hybrid_idx::kpool_layout::seq & sq, uint32_t cell) {
return (int64_t) sq.strm*kv_size + cell;
};
std::vector<uint8_t> seq_in_ub(LLAMA_MAX_SEQ, 0);
for (uint32_t i = 0; i < n_tokens; ++i) {
for (int32_t k = 0; k < ubatch->n_seq_id[i]; ++k) {
seq_in_ub[ubatch->seq_id[i][k]] = 1;
}
}
int64_t dummy_cell = 0;
{
const llama_seq_id s = ubatch->seq_id[0][0];
const auto & sq = lay.seqs[s];
auto it = std::lower_bound(sq.cells.begin(), sq.cells.end(), std::make_pair(ubatch->pos[0], 0u));
GGML_ASSERT(it != sq.cells.end() && it->first == ubatch->pos[0]);
dummy_cell = gcell(sq, it->second);
}
std::vector<int64_t> rank;
if (by_order) {
const auto & sinfo = sinfos_kpool[i_cur];
const uint32_t n_tps = (uint32_t) sinfo.size();
rank.resize(n_tokens);
for (uint32_t i = 0; i < n_tokens; ++i) {
rank[i] = kpool_rank(lay.seqs[ubatch->seq_id[i][0]].cells, ubatch->pos[i], sinfo.idxs[i / n_tps][i % n_tps]);
GGML_ASSERT(rank[i] >= 0);
}
}
const int32_t sentinel = (int32_t) n_kv;
float * gm = nullptr;
uint32_t n_sel = 0;
uint32_t n_top = 0; if (sel_mask != nullptr) {
GGML_ASSERT(ggml_backend_buffer_is_host(sel_mask->buffer));
GGML_ASSERT(sel_mask->type == GGML_TYPE_F32);
GGML_ASSERT(sel_mask->ne[3] == (int64_t) n_tokens && sel_mask->ne[1] == 1 && sel_mask->ne[2] == 1);
n_sel = (uint32_t) sel_mask->ne[0];
n_top = n_sel / kpool;
GGML_ASSERT(n_sel % kpool == 0 || n_sel % kpool == kpool - 1);
gm = (float *) sel_mask->data;
}
std::vector<uint32_t> seq_pool_start(LLAMA_MAX_SEQ, 0);
std::vector<llama_pos> pool_end;
pool_end.reserve(n_pool);
int32_t * pcell = (int32_t *) pool_cells->data;
int32_t * pidx = (int32_t *) pool_idxs->data;
int32_t * nidx = (int32_t *) new_pool_idxs->data;
int64_t * nrep = (int64_t *) new_pool_rep->data;
int32_t * npos = new_pool_pos != nullptr ? (int32_t *) new_pool_pos->data : nullptr;
if (npos != nullptr) {
std::fill(npos, npos + 4*n_new_g, 0);
}
uint32_t i_new = 0;
for (llama_seq_id s = 0; s < LLAMA_MAX_SEQ; ++s) {
const auto & sq = lay.seqs[s];
seq_pool_start[s] = (uint32_t) pool_end.size();
const bool inert = n_stream_kv > 1 && !seq_in_ub[s];
for (size_t pi = 0; pi < sq.pools.size(); ++pi) {
const uint32_t j = sq.pools[pi];
const uint32_t ip = (uint32_t) pool_end.size();
GGML_ASSERT(ip + 1 < n_pool);
const uint32_t rep = sq.cells[j + kpool - 1].second;
pcell[ip] = (int32_t) gcell(sq, rep);
for (uint32_t k = 0; k < kpool; ++k) {
pidx[(size_t) ip*kpool + k] = inert ? sentinel : (int32_t) sq.cells[j + k].second;
}
if (st.is_new[ip] == st.generation) {
GGML_ASSERT(i_new < n_new);
for (uint32_t k = 0; k < kpool; ++k) {
nidx[(size_t) i_new*kpool + k] = (int32_t) gcell(sq, sq.cells[j + k].second);
}
nrep[i_new] = gcell(sq, rep);
if (npos != nullptr) {
const uint32_t c = sq.cells[j].second;
const auto & e = mem->get_mem_idx()->get_cells(s).ext_get(c);
npos[0*n_new_g + i_new] = sq.cells[j].first;
npos[1*n_new_g + i_new] = e.y;
npos[2*n_new_g + i_new] = e.x;
npos[3*n_new_g + i_new] = sq.cells[j].first;
}
++i_new;
}
pool_end.push_back(sq.cells[j + kpool - 1].first);
}
}
GGML_ASSERT(i_new == n_new);
if (n_new_g > n_new) {
std::vector<int64_t> reps(pcell, pcell + pool_end.size());
std::sort(reps.begin(), reps.end());
int64_t pad_cell = 0;
for (uint32_t i = n_new; i < n_new_g; ++i, ++pad_cell) {
while (std::binary_search(reps.begin(), reps.end(), pad_cell)) {
++pad_cell;
}
GGML_ASSERT(pad_cell < (int64_t) kv_size*n_stream_kv);
for (uint32_t k = 0; k < kpool; ++k) {
nidx[(size_t) i*kpool + k] = (int32_t) pad_cell;
}
nrep[i] = pad_cell;
}
}
const uint32_t n_pool_real = (uint32_t) pool_end.size();
for (uint32_t ip = n_pool_real; ip < n_pool; ++ip) {
pcell[ip] = (int32_t) dummy_cell; for (uint32_t k = 0; k < kpool; ++k) {
pidx[(size_t) ip*kpool + k] = sentinel;
}
}
auto fill_mask = [&](auto * data) {
using T = std::remove_pointer_t<decltype(data)>;
const T keep = llama_cast<T>(0.0f);
const T drop = llama_cast<T>(-INFINITY);
for (uint32_t i = 0; i < n_tokens; ++i) {
const llama_seq_id s = ubatch->seq_id[i][0];
const llama_pos p = ubatch->pos[i];
T * row = data + (size_t) i*n_pool;
std::fill(row, row + n_pool, drop);
const uint32_t p0 = seq_pool_start[s];
const uint32_t p1 = p0 + (uint32_t) lay.seqs[s].pools.size();
const uint32_t nv = by_order ? std::min(p1 - p0, (uint32_t) ((rank[i] + 1)/kpool)) :
(uint32_t) (std::upper_bound(pool_end.begin() + p0, pool_end.begin() + p1, p) - (pool_end.begin() + p0));
std::fill(row + p0, row + p0 + nv, keep);
if (gm != nullptr) {
const uint32_t nvc = std::min(nv, n_top);
float * grow = gm + (size_t) i*n_sel;
std::fill(grow, grow + (size_t) nvc*kpool, 0.0f);
std::fill(grow + (size_t) nvc*kpool, grow + (size_t) n_top*kpool, -INFINITY);
}
}
};
if (pool_mask->type == GGML_TYPE_F16) {
fill_mask((ggml_fp16_t *) pool_mask->data);
} else {
fill_mask((float *) pool_mask->data);
}
int32_t * tidx = (int32_t *) tail_idxs->data;
for (uint32_t i = 0; i < n_tokens; ++i) {
const llama_seq_id s = ubatch->seq_id[i][0];
const llama_pos p = ubatch->pos[i];
const auto & sq = lay.seqs[s];
const uint32_t n_tail = by_order ?
(uint32_t) ((rank[i] + 1) % kpool) :
(uint32_t) ((p - sq.pos_min + 1) % (llama_pos) kpool);
for (uint32_t k = 0; k < kpool - 1; ++k) {
int32_t cell = sentinel;
bool real = false;
if (k < n_tail && by_order) {
const uint32_t c = sq.cells[rank[i] - k].second;
cell = (int32_t) c;
real = true;
} else if (k < n_tail) {
const llama_pos pt = p - (llama_pos) k;
auto it = std::lower_bound(sq.cells.begin(), sq.cells.end(), std::make_pair(pt, 0u));
if (it != sq.cells.end() && it->first == pt) {
cell = (int32_t) it->second;
real = true;
}
}
tidx[(size_t) i*(kpool - 1) + k] = cell;
if (gm != nullptr && n_sel % kpool != 0) {
gm[(size_t) i*n_sel + (size_t) n_top*kpool + k] = real ? 0.0f : -INFINITY;
}
}
}
}