if (p1 < 0) {
p1 = std::numeric_limits<llama_pos>::max();
}
// models like Mamba or RWKV can't have a state partially erased if (seq_id >= (int64_t) size) { // could be fatal returnfalse;
} if (0 <= seq_id) {
int32_t & tail_id = cells[seq_id].tail; if (tail_id >= 0) { constauto & cell = cells[tail_id]; // partial intersection is invalid if ((0 < p0 && p0 <= cell.pos) || (0 < p1 && p1 <= cell.pos)) { returnfalse;
} // invalidate tails which will be cleared if (p0 <= cell.pos && cell.pos < p1) {
tail_id = -1;
}
}
} else { // seq_id is negative, then the range should include everything or nothing if (p0 != p1 && (p0 != 0 || p1 != std::numeric_limits<llama_pos>::max())) { returnfalse;
}
}
for (uint32_t i = 0; i < size; ++i) { if (cells[i].pos >= p0 && cells[i].pos < p1) { if (seq_id < 0) {
cells[i].seq_id.clear();
} elseif (cells[i].has_seq_id(seq_id)) {
cells[i].seq_id.erase(seq_id);
} else { continue;
} if (cells[i].is_empty()) { // keep count of the number of used cells if (cells[i].pos >= 0) {
used--;
}
cells[i].pos = -1;
cells[i].src = -1; if (new_head == size) {
new_head = i;
}
}
}
}
// If we freed up a slot, set head to it so searching can start there. if (new_head != size && new_head < head) {
head = new_head;
}
std::vector<llama_ubatch> ubatches; while (true) {
llama_ubatch ubatch;
if (embd_all) { // if all tokens are output, split by sequence
ubatch = balloc.split_seq(n_ubatch);
} else {
ubatch = balloc.split_equal(n_ubatch, false);
}
bool llama_memory_recurrent::prepare(const std::vector<llama_ubatch> & ubatches) { // simply remember the full state because it is very small for this type of cache // TODO: optimize auto org_cells = cells; auto org_used = used; auto org_head = head;
bool success = true;
for (constauto & ubatch : ubatches) { if (!find_slot(ubatch)) {
success = false; break;
}
}
// restore the original state
cells = std::move(org_cells);
used = org_used;
head = org_head;
// if we have enough unused cells before the current head -> // better to start searching from the beginning of the cache, hoping to fill it if (head > used + 2*n_seqs) {
head = 0;
}
// For recurrent state architectures (like Mamba or RWKV), // each cache cell can store the state for a whole sequence. // A slot should be always be contiguous.
// can only process batches with an equal number of new tokens in each sequence
GGML_ASSERT(ubatch.equal_seqs());
int32_t min = size - 1;
int32_t max = 0;
// everything should fit if all seq_ids are smaller than the max for (uint32_t s = 0; s < n_seqs; ++s) { const uint32_t i = s*n_seq_tokens; // first token of sequence set s const uint32_t n_seq_id = ubatch.n_seq_id[i];
if (seq_id < 0 || (uint32_t) seq_id >= size) { // too big seq_id // TODO: would it be possible to resize the cache instead?
LLAMA_LOG_ERROR("%s: seq_id=%d >= n_seq_max=%u Try using a bigger --parallel value\n", __func__, seq_id, n_seq_max); returnfalse;
} if (j > 0) { auto & seq = cells[seq_id]; if (seq.tail >= 0) { auto & cell = cells[seq.tail]; // clear cells from seq_ids that become shared // (should not normally happen, but let's handle it anyway)
cell.seq_id.erase(seq_id);
seq.tail = -1; if (cell.seq_id.empty()) {
cell.pos = -1;
cell.src = -1;
used -= 1;
}
}
}
}
}
#ifndef NDEBUG
{
std::vector<int32_t> tails_verif;
tails_verif.assign(size, -1); for (uint32_t i = 0; i < size; ++i) { auto & cell = cells[i]; for (llama_seq_id seq_id : cell.seq_id) { if (tails_verif[seq_id] != -1) {
LLAMA_LOG_ERROR("%s: duplicate tail for seq_id %d in cell %d and %d\n", __func__, seq_id, i, tails_verif[seq_id]);
}
tails_verif[seq_id] = i;
}
} for (uint32_t i = 0; i < size; ++i) { if (tails_verif[i] != cells[i].tail) {
LLAMA_LOG_ERROR("%s: wrong tail for seq_id %d, (%d instead of %d)\n", __func__, i, cells[i].tail, tails_verif[i]);
}
}
} #endif
// find next empty cell
uint32_t next_empty_cell = head;
for (uint32_t i = 0; i < size; ++i) { if (next_empty_cell >= size) { next_empty_cell -= size; } auto & cell = cells[next_empty_cell]; if (cell.is_empty()) { break; }
next_empty_cell += 1;
}
// find usable cell range for (uint32_t s = 0; s < n_seqs; ++s) { const uint32_t i = s*n_seq_tokens; const llama_seq_id seq_id = ubatch.seq_id[i][0]; auto & seq_meta = cells[seq_id]; bool has_cell = false; if (seq_meta.tail >= 0) { auto & cell = cells[seq_meta.tail];
GGML_ASSERT(cell.has_seq_id(seq_id)); // does this seq_id "own" the cell? if (cell.seq_id.size() == 1) { has_cell = true; }
} if (!has_cell) { auto & empty_cell = cells[next_empty_cell];
GGML_ASSERT(empty_cell.is_empty()); // copy old tail into the empty cell if (seq_meta.tail >= 0) { auto & orig_cell = cells[seq_meta.tail];
empty_cell.pos = orig_cell.pos;
empty_cell.src = orig_cell.src;
orig_cell.seq_id.erase(seq_id);
empty_cell.seq_id.insert(seq_id); // will be overwritten
GGML_ASSERT(!orig_cell.is_empty()); // has at least one remaining seq_id
}
seq_meta.tail = next_empty_cell; // find next empty cell if (s + 1 < n_seqs) { for (uint32_t j = 0; j < size; ++j) {
next_empty_cell += 1; if (next_empty_cell >= size) { next_empty_cell -= size; } auto & cell = cells[next_empty_cell]; if (cell.is_empty()) { break; }
}
}
} if (min > seq_meta.tail) { min = seq_meta.tail; } if (max < seq_meta.tail) { max = seq_meta.tail; }
}
// gather and re-order for (uint32_t s = 0; s < n_seqs; ++s) { const uint32_t i = s*n_seq_tokens; const int32_t dst_id = s + min; const int32_t src_id = cells[ubatch.seq_id[i][0]].tail; if (dst_id != src_id) { auto & dst_cell = cells[dst_id]; auto & src_cell = cells[src_id];
// update the pos of the used seqs for (uint32_t s = 0; s < n_seqs; ++s) { const uint32_t i = s*n_seq_tokens; const llama_pos last_pos = ubatch.pos[i + n_seq_tokens - 1]; const int32_t cell_id = s + min; auto & cell = cells[cell_id];
if (cell.pos >= 0 && last_pos != cell.pos + (llama_pos) n_seq_tokens) { // What should happen when the pos backtracks or skips a value? // Clearing the state mid-batch would require special-casing which isn't done.
LLAMA_LOG_WARN("%s: non-consecutive token position %d after %d for sequence %d with %u new tokens\n",
__func__, last_pos, cell.pos, ubatch.seq_id[i][0], n_seq_tokens);
}
cell.pos = last_pos;
cell.seq_id.clear(); for (int32_t j = 0; j < ubatch.n_seq_id[i]; ++j) { const llama_seq_id seq_id = ubatch.seq_id[i][j];
cell.seq_id.insert(seq_id);
cells[seq_id].tail = cell_id;
}
}
// Find first cell without src refs, to use as the zero-ed state
{ // TODO: bake-in src refcounts in the cell metadata
std::vector<int32_t> refcounts(size, 0); for (size_t i = 0; i < size; ++i) { const int32_t src = cells[i].src; if (src >= 0) {
refcounts[src] += 1;
}
}
rs_z = -1; for (int i = min; i <= max; ++i) { if (refcounts[i] == 0) {
rs_z = i; break;
}
}
for (int i = min; i <= max; ++i) { if (cells[i].src < 0) {
GGML_ASSERT(rs_z >= 0);
cells[i].src0 = rs_z;
} else { // Stage the source ids for all used cells to allow correct seq_* behavior // and still make these values available when setting the inputs
cells[i].src0 = cells[i].src;
}
cells[i].src = i; // avoid moving or clearing twice
}
}
// allow getting the range of used cells, from head to head + n
head = min;
n = max - min + 1;
used = std::count_if(cells.begin(), cells.end(),
[](const mem_cell & cell){ return !cell.is_empty(); });
// sanity check return n >= n_seqs;
}
bool llama_memory_recurrent::get_can_shift() const { // shifting the pos is trivial for recurrent models return true;
}
std::vector<std::pair<uint32_t, uint32_t>> cell_ranges; // ranges, from inclusive, to exclusive
uint32_t cell_count = 0;
// Count the number of cells with the specified seq_id // Find all the ranges of cells with this seq id (or all, when -1)
uint32_t cell_range_begin = size; for (uint32_t i = 0; i < size; ++i) { constauto & cell = cells[i]; if ((seq_id == -1 && !cell.is_empty()) || cell.has_seq_id(seq_id)) {
++cell_count; if (cell_range_begin == size) {
cell_range_begin = i;
}
} else { if (cell_range_begin != size) {
cell_ranges.emplace_back(cell_range_begin, i);
cell_range_begin = size;
}
}
} if (cell_range_begin != size) {
cell_ranges.emplace_back(cell_range_begin, size);
}
// DEBUG CHECK: Sum of cell counts in ranges should equal the total cell count
uint32_t cell_count_check = 0; for (constauto & range : cell_ranges) {
cell_count_check += range.second - range.first;
}
GGML_ASSERT(cell_count == cell_count_check);
// Iterate and write all the keys first, each row is a cell // Get whole range at a time for (uint32_t il = 0; il < n_layer; ++il) { // skip null layers (read_data will handle this by checking "r_l" and "s_l" for null) if (r_l[il] == nullptr) continue;
// Read each range of cells of k_size length each into tmp_buf and write out for (constauto & range : cell_ranges) { const size_t range_size = range.second - range.first; const size_t buf_size = range_size * r_size_row;
io.write_tensor(r_l[il], range.first * r_size_row, buf_size);
}
}
if (!s_trans) { for (uint32_t il = 0; il < n_layer; ++il) { // skip null layers (read_data will handle this by checking "r_l" and "s_l" for null) if (s_l[il] == nullptr) continue;
// Write value type const int32_t s_type_i = (int32_t)s_l[il]->type;
io.write(&s_type_i, sizeof(s_type_i));
// Write row size of value const uint64_t s_size_row = ggml_row_size(s_l[il]->type, hparams.n_embd_s());
io.write(&s_size_row, sizeof(s_size_row));
// Read each range of cells of s_size length each into tmp_buf and write out for (constauto & range : cell_ranges) { const size_t range_size = range.second - range.first; const size_t buf_size = range_size * s_size_row;
io.write_tensor(s_l[il], range.first * s_size_row, buf_size);
}
}
} else { // When v is transposed, we also need the element size and get the element ranges from each row const uint32_t mem_size = size; for (uint32_t il = 0; il < n_layer; ++il) { // skip null layers (read_data will handle this by checking "r_l" and "s_l" for null) if (s_l[il] == nullptr) continue;
const uint32_t n_embd_s = hparams.n_embd_s();
// Write value type const int32_t s_type_i = (int32_t)s_l[il]->type;
io.write(&s_type_i, sizeof(s_type_i));
// For each row, we get the element values of each cell for (uint32_t j = 0; j < n_embd_s; ++j) { // Read each range of cells of v_size_el length each into tmp_buf and write out for (constauto & range : cell_ranges) { const size_t range_size = range.second - range.first; const size_t src_offset = (range.first + j * mem_size) * s_size_el; const size_t buf_size = range_size * s_size_el;
io.write_tensor(s_l[il], src_offset, buf_size);
}
}
}
}
}
bool llama_memory_recurrent::state_read_meta(llama_io_read_i & io, uint32_t cell_count, llama_seq_id dest_seq_id) { if (dest_seq_id != -1) { // single sequence
// TODO: llama_memory_recurrent should have a notion of max sequences //if (seq_id < 0 || (uint32_t) seq_id >= llama_n_seq_max(ctx)) { if (seq_id < 0) { //LLAMA_LOG_ERROR("%s: invalid seq_id, %d is out of range [0, %u)\n", __func__, seq_id, llama_n_seq_max(ctx));
LLAMA_LOG_ERROR("%s: invalid seq_id, %d is out of range [0, inf)\n", __func__, seq_id); returnfalse;
}
cell.seq_id.insert(seq_id);
int32_t & tail = cells[seq_id].tail; if (tail != -1) {
LLAMA_LOG_ERROR("%s: duplicate tail for seq_id %d in cell %d and %d\n", __func__, seq_id, i, tail); returnfalse;
}
tail = i;
}
}
head = 0;
used = cell_count;
}
for (uint32_t i = 0; i < cell_count; ++i) {
uint32_t cell_id = head + i; // make sure the recurrent states will keep their restored state
cells[cell_id].src = cell_id;
}
if (n_layer != hparams.n_layer) {
LLAMA_LOG_ERROR("%s: mismatched layer count (%u instead of %u)\n", __func__, n_layer, hparams.n_layer); returnfalse;
} if (cell_count > size) {
LLAMA_LOG_ERROR("%s: not enough cells in kv cache to restore state (%u > %u)\n", __func__, cell_count, size); returnfalse;
} if (false != (bool) s_trans) {
LLAMA_LOG_ERROR("%s: incompatible s transposition\n", __func__); returnfalse;
}
// For each layer, read the keys for each cell, one row is one cell, read as one contiguous block for (uint32_t il = 0; il < n_layer; ++il) { // skip null layers if (r_l[il] == nullptr) continue;
// Read type of key
int32_t r_type_i_ref;
io.read_to(&r_type_i_ref, sizeof(r_type_i_ref)); const int32_t r_type_i = (int32_t) r_l[il]->type; if (r_type_i != r_type_i_ref) {
LLAMA_LOG_ERROR("%s: mismatched r type (%d != %d, layer %d)\n", __func__, r_type_i, r_type_i_ref, il); returnfalse;
}
if (cell_count) { // Read and set the keys for the whole cell range
ggml_backend_tensor_set(r_l[il], io.read(cell_count * r_size_row), head * r_size_row, cell_count * r_size_row);
}
}
if (!s_trans) { for (uint32_t il = 0; il < n_layer; ++il) { // skip null layers if (s_l[il] == nullptr) continue;
// Read type of value
int32_t s_type_i_ref;
io.read_to(&s_type_i_ref, sizeof(s_type_i_ref)); const int32_t s_type_i = (int32_t)s_l[il]->type;
if (s_type_i != s_type_i_ref) {
LLAMA_LOG_ERROR("%s: mismatched s type (%d != %d, layer %d)\n", __func__, s_type_i, s_type_i_ref, il); returnfalse;
}
// Read row size of value
uint64_t s_size_row_ref;
io.read_to(&s_size_row_ref, sizeof(s_size_row_ref)); const size_t s_size_row = ggml_row_size(s_l[il]->type, hparams.n_embd_s()); if (s_size_row != s_size_row_ref) {
LLAMA_LOG_ERROR("%s: mismatched s row size (%zu != %zu, layer %d)\n", __func__, s_size_row, (size_t) s_size_row_ref, il); returnfalse;
}
if (cell_count) { // Read and set the values for the whole cell range
ggml_backend_tensor_set(s_l[il], io.read(cell_count * s_size_row), head * s_size_row, cell_count * s_size_row);
}
}
} else { // For each layer, read the values for each cell (transposed) for (uint32_t il = 0; il < n_layer; ++il) { // skip null layers if (s_l[il] == nullptr) continue;
const uint32_t n_embd_s = hparams.n_embd_s();
// Read type of value
int32_t s_type_i_ref;
io.read_to(&s_type_i_ref, sizeof(s_type_i_ref)); const int32_t s_type_i = (int32_t)s_l[il]->type; if (s_type_i != s_type_i_ref) {
LLAMA_LOG_ERROR("%s: mismatched s type (%d != %d, layer %d)\n", __func__, s_type_i, s_type_i_ref, il); returnfalse;
}
// Read element size of value
uint32_t s_size_el_ref;
io.read_to(&s_size_el_ref, sizeof(s_size_el_ref)); const size_t s_size_el = ggml_type_size(s_l[il]->type); if (s_size_el != s_size_el_ref) {
LLAMA_LOG_ERROR("%s: mismatched s element size (%zu != %zu, layer %d)\n", __func__, s_size_el, (size_t) s_size_el_ref, il); returnfalse;
}
// Read state embedding size
uint32_t n_embd_s_ref;
io.read_to(&n_embd_s_ref, sizeof(n_embd_s_ref)); if (n_embd_s != n_embd_s_ref) {
LLAMA_LOG_ERROR("%s: mismatched s embedding size (%u != %u, layer %d)\n", __func__, n_embd_s, n_embd_s_ref, il); returnfalse;
}
if (cell_count) { // For each row in the transposed matrix, read the values for the whole cell range for (uint32_t j = 0; j < n_embd_s; ++j) { const size_t dst_offset = (head + j * size) * s_size_el;
ggml_backend_tensor_set(s_l[il], io.read(cell_count * s_size_el), dst_offset, cell_count * s_size_el);
}
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.