Skip to content

Commit a386cd7

Browse files
committed
kpool init apply
1 parent 9fe9fd7 commit a386cd7

3 files changed

Lines changed: 172 additions & 151 deletions

File tree

src/llama-memory-hybrid-idx.cpp

Lines changed: 145 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ llama_memory_hybrid_idx::llama_memory_hybrid_idx(
6666
model, hparams_idx, type_k, type_v, v_trans, offload, unified,
6767
kv_size, n_seq_max, n_pad, n_swa, swa_type,
6868
nullptr, filter_idx, nullptr, nullptr, "idx_");
69-
}()) {}
69+
}()),
70+
n_kpool(model.hparams.indexer_kpool) {}
7071

7172
llama_memory_context_ptr llama_memory_hybrid_idx::init_batch(llama_batch_allocr & balloc, uint32_t n_ubatch, bool embd_all) {
7273
// note: repeats llama_memory_hybrid::init_batch, as the indexer needs the attention slot infos that the base context hides
@@ -610,6 +611,31 @@ static std::vector<uint32_t> llama_memory_hybrid_idx_ns(const llama_kv_cache::sl
610611
return res;
611612
}
612613

614+
// The kpool layout of one ubatch.
615+
struct llama_memory_hybrid_idx_context::kpool_state {
616+
struct seq {
617+
llama_pos pos_min = 0;
618+
std::vector<std::pair<llama_pos, uint32_t>> cells; // Position and cell pairs, sorted by position
619+
std::vector<uint32_t> pools;
620+
std::vector<uint8_t> is_new;
621+
};
622+
623+
std::vector<seq> seqs;
624+
625+
uint32_t n_pool_real = 0;
626+
uint32_t n_new = 0;
627+
bool cache_safe = true;
628+
};
629+
630+
namespace {
631+
632+
// The last padded pool is always unused.
633+
uint32_t kpool_pad(uint32_t n_pool) {
634+
return std::max<uint32_t>(64u, GGML_PAD(n_pool + 1, 64u));
635+
}
636+
637+
}
638+
613639
llama_memory_hybrid_idx_context::llama_memory_hybrid_idx_context(llama_memory_status status) :
614640
llama_memory_hybrid_context(status) {}
615641

@@ -621,7 +647,11 @@ llama_memory_hybrid_idx_context::llama_memory_hybrid_idx_context(llama_memory_hy
621647
ns_ubatch(mem->get_mem_idx() == nullptr ?
622648
std::vector<uint32_t>() : std::vector<uint32_t>{ mem->get_mem_idx()->get_n_stream() }),
623649
ctx_idx(mem->get_mem_idx() == nullptr ? nullptr :
624-
new llama_kv_cache_context(mem->get_mem_idx())) {}
650+
new llama_kv_cache_context(mem->get_mem_idx())) {
651+
if (mem->get_mem_idx() != nullptr && mem->get_kpool() > 0) {
652+
kpool_states.push_back(kpool_build_state(nullptr));
653+
}
654+
}
625655

626656
llama_memory_hybrid_idx_context::llama_memory_hybrid_idx_context(
627657
llama_memory_hybrid_idx * mem,
@@ -644,6 +674,7 @@ llama_memory_hybrid_idx_context::llama_memory_hybrid_idx_context(
644674
ns_ubatch(llama_memory_hybrid_idx_ns(sinfos_idx)),
645675
ctx_idx(mem->get_mem_idx() == nullptr ? nullptr :
646676
new llama_kv_cache_context(mem->get_mem_idx(), std::move(sinfos_idx), ubatches)) {
677+
kpool_track = mem->get_mem_idx() != nullptr && mem->get_kpool() > 0;
647678
// Sequence edits require a full re-pool.
648679
kpool_dirty_batch = mem->kpool_is_dirty();
649680
}
@@ -672,6 +703,14 @@ bool llama_memory_hybrid_idx_context::apply() {
672703
res = res & ctx_idx->apply();
673704
}
674705

706+
// Fix the pool layout of this ubatch.
707+
if (res && kpool_track) {
708+
GGML_ASSERT(i_cur <= kpool_states.size());
709+
kpool_states.resize(i_cur);
710+
711+
kpool_states.push_back(kpool_build_state(&get_ubatch()));
712+
}
713+
675714
return res;
676715
}
677716

@@ -700,188 +739,163 @@ void llama_memory_hybrid_idx_context::set_input_qsa(
700739

701740
// k-pool DSA indexer (glm5-next)
702741

703-
// Cache the k-pool layout for this ubatch.
704-
struct llama_memory_hybrid_idx_context::kpool_state {
705-
struct seq {
706-
llama_pos pos_min = 0;
707-
std::vector<std::pair<llama_pos, uint32_t>> cells; // Position and cell pairs, sorted by position
708-
std::vector<uint32_t> pools;
709-
std::vector<uint8_t> is_new;
710-
};
711-
712-
std::vector<seq> seqs;
713-
714-
uint32_t n_pool_real = 0;
715-
uint32_t n_new = 0;
716-
bool cache_safe = true;
717-
718-
bool have_new = false; // Whether the ubatch-dependent part is filled.
719-
size_t i_ubatch = SIZE_MAX; // The ubatch this state was computed for.
720-
};
721-
722-
namespace {
723-
724-
// The last padded pool is always unused.
725-
uint32_t kpool_pad(uint32_t n_pool) {
726-
return std::max<uint32_t>(64u, GGML_PAD(n_pool + 1, 64u));
727-
}
728-
729-
}
730-
731-
llama_memory_hybrid_idx_context::kpool_state & llama_memory_hybrid_idx_context::kpool_get_state(
732-
uint32_t kpool, const llama_ubatch * ubatch) const {
742+
llama_memory_hybrid_idx_context::kpool_state llama_memory_hybrid_idx_context::kpool_build_state(
743+
const llama_ubatch * ubatch) const {
733744
GGML_ASSERT(mem != nullptr && mem->get_mem_idx() != nullptr);
734745
GGML_ASSERT(get_n_stream() == 1 && "TODO: k-pool indexer with multiple streams");
735746

736-
if (!kpool_st || kpool_st->i_ubatch != i_cur) {
737-
kpool_st = std::make_unique<kpool_state>();
747+
const uint32_t kpool = mem->get_kpool();
738748

739-
auto & st = *kpool_st;
740-
st.i_ubatch = i_cur;
741-
st.seqs.resize(LLAMA_MAX_SEQ);
749+
kpool_state st;
750+
st.seqs.resize(LLAMA_MAX_SEQ);
742751

743-
const auto & cells = mem->get_mem_idx()->get_cells(0);
752+
const auto & cells = mem->get_mem_idx()->get_cells(0);
744753

745-
const uint32_t n_kv = get_idx()->get_n_kv();
746-
const uint32_t n = std::min<uint32_t>(n_kv, cells.size());
754+
// Scan only active sequences
755+
std::vector<llama_seq_id> active;
756+
for (llama_seq_id s = 0; s < LLAMA_MAX_SEQ; ++s) {
757+
if (cells.seq_pos_min(s) >= 0) {
758+
active.push_back(s);
759+
}
760+
}
747761

748-
// Scan only active sequences
749-
std::vector<llama_seq_id> active;
750-
for (llama_seq_id s = 0; s < LLAMA_MAX_SEQ; ++s) {
751-
if (cells.seq_pos_min(s) >= 0) {
752-
active.push_back(s);
762+
for (uint32_t i = 0; i < cells.size(); ++i) {
763+
if (cells.is_empty(i)) {
764+
continue;
765+
}
766+
const llama_pos p = cells.pos_get(i);
767+
uint32_t n_seq_cell = 0;
768+
for (const llama_seq_id s : active) {
769+
if (cells.seq_has(i, s)) {
770+
st.seqs[s].cells.emplace_back(p, i);
771+
++n_seq_cell;
753772
}
754773
}
774+
if (n_seq_cell > 1) {
775+
st.cache_safe = false;
776+
}
777+
}
778+
779+
for (auto & sq : st.seqs) {
780+
if (sq.cells.empty()) {
781+
continue;
782+
}
783+
if (!std::is_sorted(sq.cells.begin(), sq.cells.end())) {
784+
std::sort(sq.cells.begin(), sq.cells.end());
785+
}
755786

756-
for (uint32_t i = 0; i < n; ++i) {
757-
if (cells.is_empty(i)) {
787+
sq.pos_min = sq.cells.front().first;
788+
789+
// Pools start at the first valid token
790+
for (size_t j = 0; j + kpool <= sq.cells.size(); ) {
791+
const llama_pos p0 = sq.cells[j].first;
792+
if ((p0 - sq.pos_min) % (llama_pos) kpool != 0) {
793+
++j;
758794
continue;
759795
}
760-
const llama_pos p = cells.pos_get(i);
761-
uint32_t n_seq_cell = 0;
762-
for (const llama_seq_id s : active) {
763-
if (cells.seq_has(i, s)) {
764-
st.seqs[s].cells.emplace_back(p, i);
765-
++n_seq_cell;
796+
bool ok = true;
797+
for (uint32_t k = 1; k < kpool; ++k) {
798+
if (sq.cells[j + k].first != p0 + (llama_pos) k) {
799+
ok = false;
800+
break;
766801
}
767802
}
768-
if (n_seq_cell > 1) {
769-
st.cache_safe = false;
803+
if (ok) {
804+
sq.pools.push_back((uint32_t) j);
805+
j += kpool;
806+
} else {
807+
++j;
770808
}
771809
}
772810

773-
for (auto & sq : st.seqs) {
774-
if (sq.cells.empty()) {
775-
continue;
776-
}
777-
if (!std::is_sorted(sq.cells.begin(), sq.cells.end())) {
778-
std::sort(sq.cells.begin(), sq.cells.end());
779-
}
780-
781-
sq.pos_min = sq.cells.front().first;
782-
783-
// Pools start at the first valid token
784-
for (size_t j = 0; j + kpool <= sq.cells.size(); ) {
785-
const llama_pos p0 = sq.cells[j].first;
786-
if ((p0 - sq.pos_min) % (llama_pos) kpool != 0) {
787-
++j;
788-
continue;
789-
}
790-
bool ok = true;
791-
for (uint32_t k = 1; k < kpool; ++k) {
792-
if (sq.cells[j + k].first != p0 + (llama_pos) k) {
793-
ok = false;
794-
break;
795-
}
796-
}
797-
if (ok) {
798-
sq.pools.push_back((uint32_t) j);
799-
j += kpool;
800-
} else {
801-
++j;
802-
}
803-
}
811+
st.n_pool_real += (uint32_t) sq.pools.size();
812+
}
804813

805-
st.n_pool_real += (uint32_t) sq.pools.size();
814+
if (ubatch == nullptr) {
815+
for (auto & sq : st.seqs) {
816+
sq.is_new.assign(sq.pools.size(), 0);
806817
}
807-
}
808818

809-
auto & st = *kpool_st;
819+
return st;
820+
}
810821

811-
if (ubatch != nullptr && !st.have_new) {
812-
// Shared cells cannot cache sequence relative pools.
813-
const bool all_new = !st.cache_safe || (kpool_dirty_batch && i_cur == 0);
822+
// Pools touched by this ubatch are re-pooled, shared cells cannot cache sequence relative pools.
823+
const bool all_new = !st.cache_safe || (kpool_dirty_batch && i_cur == 0);
814824

815-
std::vector<std::vector<llama_pos>> upos(LLAMA_MAX_SEQ);
816-
if (!all_new) {
817-
for (uint32_t i = 0; i < ubatch->n_tokens; ++i) {
818-
for (int32_t k = 0; k < ubatch->n_seq_id[i]; ++k) {
819-
upos[ubatch->seq_id[i][k]].push_back(ubatch->pos[i]);
820-
}
825+
std::vector<std::vector<llama_pos>> upos(LLAMA_MAX_SEQ);
826+
if (!all_new) {
827+
for (uint32_t i = 0; i < ubatch->n_tokens; ++i) {
828+
for (int32_t k = 0; k < ubatch->n_seq_id[i]; ++k) {
829+
upos[ubatch->seq_id[i][k]].push_back(ubatch->pos[i]);
821830
}
822-
for (auto & v : upos) {
823-
if (!std::is_sorted(v.begin(), v.end())) {
824-
std::sort(v.begin(), v.end());
825-
}
831+
}
832+
for (auto & v : upos) {
833+
if (!std::is_sorted(v.begin(), v.end())) {
834+
std::sort(v.begin(), v.end());
826835
}
827836
}
837+
}
828838

829-
for (llama_seq_id s = 0; s < LLAMA_MAX_SEQ; ++s) {
830-
auto & sq = st.seqs[s];
839+
for (llama_seq_id s = 0; s < LLAMA_MAX_SEQ; ++s) {
840+
auto & sq = st.seqs[s];
831841

832-
sq.is_new.assign(sq.pools.size(), all_new ? 1 : 0);
833-
if (all_new) {
834-
st.n_new += (uint32_t) sq.pools.size();
835-
continue;
836-
}
842+
sq.is_new.assign(sq.pools.size(), all_new ? 1 : 0);
843+
if (all_new) {
844+
st.n_new += (uint32_t) sq.pools.size();
845+
continue;
846+
}
837847

838-
const auto & up = upos[s];
839-
if (up.empty()) {
840-
continue;
841-
}
848+
const auto & up = upos[s];
849+
if (up.empty()) {
850+
continue;
851+
}
842852

843-
for (size_t pi = 0; pi < sq.pools.size(); ++pi) {
844-
const llama_pos p0 = sq.cells[sq.pools[pi]].first;
853+
for (size_t pi = 0; pi < sq.pools.size(); ++pi) {
854+
const llama_pos p0 = sq.cells[sq.pools[pi]].first;
845855

846-
auto it = std::lower_bound(up.begin(), up.end(), p0);
847-
if (it != up.end() && *it < p0 + (llama_pos) kpool) {
848-
sq.is_new[pi] = 1;
849-
st.n_new++;
850-
}
856+
auto it = std::lower_bound(up.begin(), up.end(), p0);
857+
if (it != up.end() && *it < p0 + (llama_pos) kpool) {
858+
sq.is_new[pi] = 1;
859+
st.n_new++;
851860
}
852861
}
853-
854-
st.have_new = true;
855862
}
856863

857864
return st;
858865
}
859866

860-
uint32_t llama_memory_hybrid_idx_context::get_n_kpool(uint32_t kpool) const {
861-
return kpool_pad(kpool_get_state(kpool, nullptr).n_pool_real);
867+
const llama_memory_hybrid_idx_context::kpool_state & llama_memory_hybrid_idx_context::kpool_cur() const {
868+
GGML_ASSERT(i_cur < kpool_states.size() && "k-pool state read before apply()");
869+
870+
return kpool_states[i_cur];
871+
}
872+
873+
uint32_t llama_memory_hybrid_idx_context::get_n_kpool() const {
874+
return kpool_pad(kpool_cur().n_pool_real);
862875
}
863876

864-
uint32_t llama_memory_hybrid_idx_context::get_n_kpool_new(uint32_t kpool, const llama_ubatch * ubatch) const {
865-
return kpool_get_state(kpool, ubatch).n_new;
877+
uint32_t llama_memory_hybrid_idx_context::get_n_kpool_new() const {
878+
return kpool_cur().n_new;
866879
}
867880

868-
bool llama_memory_hybrid_idx_context::get_kpool_cache_safe(uint32_t kpool) const {
869-
return kpool_get_state(kpool, nullptr).cache_safe;
881+
bool llama_memory_hybrid_idx_context::get_kpool_cache_safe() const {
882+
return kpool_cur().cache_safe;
870883
}
871884

872885
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,
873886
ggml_tensor * gather_mask, bool gather, ggml_tensor * new_pool_idxs, ggml_tensor * new_pool_rep,
874-
const llama_ubatch * ubatch, uint32_t kpool) const {
887+
const llama_ubatch * ubatch) const {
875888
GGML_ASSERT(mem != nullptr && mem->get_mem_idx() != nullptr);
876889
GGML_ASSERT(get_n_stream() == 1 && "TODO: k-pool indexer with multiple streams");
877890
GGML_ASSERT(ggml_backend_buffer_is_host(pool_cells->buffer));
878891
GGML_ASSERT(ggml_backend_buffer_is_host(pool_idxs->buffer));
879892
GGML_ASSERT(ggml_backend_buffer_is_host(pool_mask->buffer));
880893
GGML_ASSERT(ggml_backend_buffer_is_host(tail_idxs->buffer));
881894

882-
const uint32_t n_kv = get_idx()->get_n_kv();
895+
const uint32_t kpool = mem->get_kpool();
896+
const uint32_t n_kv = get_idx()->get_n_kv();
883897

884-
const auto & st = kpool_get_state(kpool, ubatch);
898+
const auto & st = kpool_cur();
885899

886900
const uint32_t n_tokens = ubatch->n_tokens;
887901
const uint32_t n_pool = (uint32_t) pool_cells->ne[0];

0 commit comments

Comments
 (0)