@@ -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
7172llama_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+
613639llama_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
626656llama_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
872885void 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