Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 36 additions & 13 deletions src/llama-kv-cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1866,29 +1866,36 @@ void llama_kv_cache::get_prev_tokens(const llama_ubatch & ubatch, uint32_t n, st
std::array<std::pair<llama_pos, llama_token>, LLAMA_MAX_SEQ> below;
below.fill({ -1, LLAMA_TOKEN_NULL });

const auto collect = [&](llama_seq_id seq_id, llama_pos pos, llama_token tok) {
if (pos >= w0) {
hist[key(seq_id, pos)] = tok;
} else if (pos > below[seq_id].first) {
below[seq_id] = { pos, tok };
}
};

// cells below w0 only answer a lookup that misses the window, which needs an M-RoPE gap
const llama_pos p_scan0 = std::max<llama_pos>(0, w0);

for (uint32_t s = 0; s < n_stream; ++s) {
// p_max inclusive: an embd token looks up cells at its own (shared) position
v_cells[s].for_each_token_in(seqs, 0, p_max + 1,
[&](llama_seq_id seq_id, llama_pos pos, llama_token tok) {
if (pos >= w0) {
hist[key(seq_id, pos)] = tok;
} else if (pos > below[seq_id].first) {
below[seq_id] = { pos, tok };
}
});
v_cells[s].for_each_token_in(seqs, p_scan0, p_max + 1, collect);
}

// the token at pos p, or the nearest earlier one when p falls in an M-RoPE gap
const auto lookup = [&](llama_seq_id seq_id, llama_pos p) -> llama_token {
// false means nothing in [w0, p]; the caller must then fall back to below[]
const auto lookup = [&](llama_seq_id seq_id, llama_pos p, llama_token & out) -> bool {
for (llama_pos q = p; q >= w0; --q) {
const auto it = hist.find(key(seq_id, q));
if (it != hist.end()) {
return it->second;
out = it->second;
return true;
}
}
return below[seq_id].second;
return false;
};

std::vector<std::pair<uint32_t, llama_seq_id>> missed;

// an embd (multimodal) ubatch can repeat one position for a whole image, so positions
// do not encode the token order; resolve its predecessors by ubatch order instead
std::vector<uint32_t> ord; // index among the ubatch tokens of the same seq
Expand Down Expand Up @@ -1925,9 +1932,25 @@ void llama_kv_cache::get_prev_tokens(const llama_ubatch & ubatch, uint32_t n, st
continue;
}

res[i*n + j] = lookup(seq_id, p);
if (!lookup(seq_id, p, res[i*n + j])) {
missed.push_back({ i*n + j, seq_id });
}
}
}

if (missed.empty()) {
return;
}

if (p_scan0 > 0) {
for (uint32_t s = 0; s < n_stream; ++s) {
v_cells[s].for_each_token_in(seqs, 0, p_scan0, collect);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid repeating the full cache traversal on fallback

When an M-RoPE lookup misses, this fallback makes a second complete traversal of the cache: llama_kv_cells::for_each_token_in() iterates every entry in used and only then filters by position (src/llama-kv-cells.h:317-319). The previous implementation traversed used once, whereas the new miss path traverses it twice while invoking collect for the same total set of cells, so multimodal batches with position gaps regress at large contexts; use a position-indexed range traversal or preserve the below-window result from the first traversal.

Useful? React with 👍 / 👎.

}
}

for (const auto & [idx, seq_id] : missed) {
res[idx] = below[seq_id].second;
}
}

size_t llama_kv_cache::total_size() const {
Expand Down
Loading