Skip to content

Commit 9b11fe7

Browse files
committed
server: anchor reusable prefix checkpoints
1 parent 001ee99 commit 9b11fe7

2 files changed

Lines changed: 44 additions & 25 deletions

File tree

examples/speculative-simple/speculative-simple.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ int main(int argc, char ** argv) {
172172
ckpt.update_pos(
173173
prompt_tgt.size(),
174174
llama_memory_seq_pos_min(llama_get_memory(ctx_tgt), seq_id),
175-
llama_memory_seq_pos_max(llama_get_memory(ctx_tgt), seq_id));
175+
llama_memory_seq_pos_max(llama_get_memory(ctx_tgt), seq_id),
176+
prompt_tgt.size());
176177

177178
if (use_ckpt_dft) {
178179
ckpt.update_dft(ctx_dft.get(), seq_id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);

tools/server/server-context.cpp

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <exception>
2222
#include <memory>
2323
#include <filesystem>
24+
#include <limits>
2425
#include <utility>
2526
#include <fstream>
2627

@@ -2122,30 +2123,35 @@ struct server_context_impl {
21222123
// n_tokens_cur: the number of tokens added to the batch for the current slot
21232124
void create_checkpoint(server_slot & slot, const int64_t n_tokens_cur, llama_pos pos_min, llama_pos pos_max) {
21242125
while (slot.prompt.checkpoints.size() >= (size_t) params_base.n_ctx_checkpoints) {
2125-
// Preserve the oldest anchor and the most recent checkpoint. Removing the checkpoint
2126+
// Preserve early prefix anchors and the most recent checkpoints. Removing a checkpoint
21262127
// from the densest interior interval keeps coverage across the full prompt history.
21272128
auto erase_it = slot.prompt.checkpoints.begin();
21282129

2129-
if (slot.prompt.checkpoints.size() > 2) {
2130-
auto prev_it = slot.prompt.checkpoints.begin();
2130+
if (slot.prompt.checkpoints.size() > 4) {
2131+
const size_t n_keep_front = 2;
2132+
const size_t n_keep_back = 2;
2133+
2134+
auto prev_it = std::next(slot.prompt.checkpoints.begin(), n_keep_front - 1);
21312135
auto cur_it = std::next(prev_it);
21322136
auto next_it = std::next(cur_it);
2137+
auto last_candidate = std::prev(slot.prompt.checkpoints.end(), n_keep_back);
21332138

21342139
erase_it = cur_it;
2135-
int64_t min_merged_span = next_it->n_tokens - prev_it->n_tokens;
2136-
2137-
while (std::next(next_it) != slot.prompt.checkpoints.end()) {
2138-
++prev_it;
2139-
++cur_it;
2140-
++next_it;
2141-
2140+
int64_t min_merged_span = std::numeric_limits<int64_t>::max();
2141+
while (cur_it != last_candidate) {
21422142
const int64_t merged_span = next_it->n_tokens - prev_it->n_tokens;
21432143

21442144
if (merged_span < min_merged_span) {
21452145
erase_it = cur_it;
21462146
min_merged_span = merged_span;
21472147
}
2148+
2149+
++prev_it;
2150+
++cur_it;
2151+
++next_it;
21482152
}
2153+
} else if (slot.prompt.checkpoints.size() > 2) {
2154+
erase_it = std::next(slot.prompt.checkpoints.begin());
21492155
}
21502156

21512157
const auto & cur = *erase_it;
@@ -2915,8 +2921,13 @@ struct server_context_impl {
29152921
const bool is_recurrent_or_hybrid =
29162922
llama_model_is_recurrent(model_tgt) ||
29172923
llama_model_is_hybrid(model_tgt);
2924+
const bool needs_context_checkpoints =
2925+
params_base.n_ctx_checkpoints > 0 &&
2926+
(ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL ||
2927+
ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_RS ||
2928+
n_swa > 0);
29182929
slot.n_prompt_tokens_prefix =
2919-
is_recurrent_or_hybrid && n_past > 0 ? n_past : -1;
2930+
needs_context_checkpoints && n_past > 0 ? n_past : -1;
29202931

29212932
// ref: https://github.com/ggml-org/llama.cpp/pull/24110
29222933
const bool has_new_tokens = (n_past < slot.task->n_tokens());
@@ -3115,6 +3126,25 @@ struct server_context_impl {
31153126
ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_RS ||
31163127
n_swa > 0);
31173128

3129+
// Anchor a checkpoint at the already processed common-prefix boundary. This
3130+
// is done before adding more tokens because checkpoints capture the current
3131+
// decoded memory state, not the batch that is about to be decoded.
3132+
if (do_checkpoint &&
3133+
slot.n_prompt_tokens_prefix > 0 &&
3134+
slot.prompt.n_tokens() == slot.n_prompt_tokens_prefix) {
3135+
const auto pos_min = llama_memory_seq_pos_min(llama_get_memory(ctx_tgt), slot.id);
3136+
const auto pos_max = llama_memory_seq_pos_max(llama_get_memory(ctx_tgt), slot.id);
3137+
const bool is_spaced =
3138+
slot.prompt.checkpoints.empty() ||
3139+
slot.prompt.n_tokens() > slot.prompt.checkpoints.back().n_tokens + params_base.checkpoint_min_step;
3140+
3141+
if (pos_min >= 0 && is_spaced) {
3142+
create_checkpoint(slot, 0, pos_min, pos_max);
3143+
}
3144+
3145+
slot.n_prompt_tokens_prefix = -1;
3146+
}
3147+
31183148
bool has_mtmd = false;
31193149

31203150
// check if we should process the image
@@ -3200,15 +3230,6 @@ struct server_context_impl {
32003230
break;
32013231
}
32023232

3203-
// Anchor a checkpoint at the common-prefix boundary. This avoids
3204-
// re-processing the gap to the nearest prompt-end checkpoint on
3205-
// subsequent requests that reuse the same prefix.
3206-
if (do_checkpoint &&
3207-
slot.n_prompt_tokens_prefix > 0 &&
3208-
slot.prompt.n_tokens() == slot.n_prompt_tokens_prefix) {
3209-
break;
3210-
}
3211-
32123233
// process the last few tokens of the prompt separately in order to allow for a checkpoint to be created.
32133234
// create checkpoints that many tokens before the end of the prompt:
32143235
// - 4 + n_ubatch
@@ -3235,6 +3256,7 @@ struct server_context_impl {
32353256
const auto n_tokens_cur = batch.n_tokens - n_tokens_prev;
32363257

32373258
const bool near_prompt_end = slot.task->n_tokens() < slot.prompt.n_tokens() + n_ubatch;
3259+
const int32_t n_tokens_start = slot.prompt.n_tokens() - n_tokens_cur;
32383260

32393261
// entire prompt has been processed
32403262
if (slot.prompt.n_tokens() == slot.task->n_tokens()) {
@@ -3259,10 +3281,6 @@ struct server_context_impl {
32593281
const auto pos_min = llama_memory_seq_pos_min(llama_get_memory(ctx_tgt), slot.id);
32603282
const auto pos_max = llama_memory_seq_pos_max(llama_get_memory(ctx_tgt), slot.id);
32613283

3262-
// checkpoints are created before the current batch is decoded, so
3263-
// their token position is the batch start rather than the prompt end
3264-
const int32_t n_tokens_start = slot.prompt.n_tokens() - n_tokens_cur;
3265-
32663284
{
32673285
const bool is_on_user =
32683286
n_before_user_known &&

0 commit comments

Comments
 (0)