This commit is contained in:
Owen Qwen
2026-07-29 17:17:25 -05:00
parent fdd9fcf85e
commit a673afad85
17 changed files with 1071 additions and 11 deletions
+37
View File
@@ -1234,6 +1234,15 @@ bool common_params_parse(int argc, char ** argv, common_params & params, llama_e
exit(0); exit(0);
} }
params.lr.init(); params.lr.init();
llama_token_cache_params token_cache_params = llama_token_cache_default_params();
token_cache_params.path = params.token_cache_path.empty() ? nullptr : params.token_cache_path.c_str();
token_cache_params.capacity_bytes = params.token_cache_size_mib * 1024ull * 1024ull;
if (!llama_token_cache_configure(token_cache_params) && params.token_cache_size_mib != 0) {
LOG_WRN("%s", "token cache was already initialized; keeping its existing configuration\n");
}
if (params.token_cache_clear && !llama_token_cache_clear()) {
throw std::runtime_error("failed to clear token cache");
}
} catch (const std::invalid_argument & ex) { } catch (const std::invalid_argument & ex) {
fprintf(stderr, "%s\n", ex.what()); fprintf(stderr, "%s\n", ex.what());
ctx_arg.params = params_org; ctx_arg.params = params_org;
@@ -1636,6 +1645,34 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
params.cache_ram_mib = value; params.cache_ram_mib = value;
} }
).set_env("LLAMA_ARG_CACHE_RAM").set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI})); ).set_env("LLAMA_ARG_CACHE_RAM").set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}));
add_opt(common_arg(
{"--token-cache-size"}, "N",
string_format("shared tokenizer and tokenized-text cache size in MiB (default: %llu, 0 = disable)",
(unsigned long long) params.token_cache_size_mib),
[](common_params & params, int value) {
if (value < 0) {
throw std::invalid_argument("token-cache-size must be non-negative");
}
params.token_cache_size_mib = value;
}
).set_env("LLAMA_ARG_TOKEN_CACHE_SIZE"));
add_opt(common_arg(
{"--token-cache-dir"}, "PATH",
"path to the shared tokenizer and tokenized-text cache database",
[](common_params & params, const std::string & value) {
if (value.empty()) {
throw std::invalid_argument("token-cache-dir must not be empty");
}
params.token_cache_path = value;
}
).set_env("LLAMA_ARG_TOKEN_CACHE_DIR"));
add_opt(common_arg(
{"--token-cache-clear"},
"clear the shared tokenizer and tokenized-text cache before loading the model",
[](common_params & params) {
params.token_cache_clear = true;
}
).set_env("LLAMA_ARG_TOKEN_CACHE_CLEAR"));
add_opt(common_arg( add_opt(common_arg(
{"-kvu", "--kv-unified"}, {"-kvu", "--kv-unified"},
{"-no-kvu", "--no-kv-unified"}, {"-no-kvu", "--no-kv-unified"},
+3
View File
@@ -626,6 +626,9 @@ struct common_params {
int32_t n_ctx_checkpoints = 32; // max number of context checkpoints per slot int32_t n_ctx_checkpoints = 32; // max number of context checkpoints per slot
int32_t checkpoint_min_step = 8192; // minimum spacing between context checkpoints int32_t checkpoint_min_step = 8192; // minimum spacing between context checkpoints
int32_t cache_ram_mib = 8192; // -1 = no limit, 0 - disable, 1 = 1 MiB, etc. int32_t cache_ram_mib = 8192; // -1 = no limit, 0 - disable, 1 = 1 MiB, etc.
uint64_t token_cache_size_mib = 5120; // shared tokenizer/text cache, 0 = disable
std::string token_cache_path;
bool token_cache_clear = false;
std::string hostname = "127.0.0.1"; std::string hostname = "127.0.0.1";
std::string public_path = ""; // NOLINT std::string public_path = ""; // NOLINT
+32
View File
@@ -0,0 +1,32 @@
# Tokenizer and tokenized-text cache
libllama maintains a persistent cache containing constructed tokenizer snapshots
and text-to-token results. The cache is enabled by default with a shared 5 GiB
logical entry budget and is available to direct libllama callers as well as the
llama.cpp tools.
The default database is `token-cache.sqlite3` below the platform llama.cpp cache
directory. `LLAMA_CACHE` changes the base directory. llama.cpp tools also accept:
```text
--token-cache-size N cache size in MiB (default: 5120, 0 disables)
--token-cache-dir PATH use a different SQLite database
--token-cache-clear clear the database before loading a model
```
The matching environment variables are `LLAMA_ARG_TOKEN_CACHE_SIZE`,
`LLAMA_ARG_TOKEN_CACHE_DIR`, and `LLAMA_ARG_TOKEN_CACHE_CLEAR`.
Cache entries are shared by processes running as the same OS user. Tokenized
text keys are SHA-256 digests and the original text is not stored. Token IDs can
usually be detokenized, however, so the database must still be treated as
sensitive user data. The cache directory and files are created with owner-only
permissions where supported.
Writes use a bounded asynchronous queue. Cache failures, unavailable storage,
or corrupt entries are treated as misses and do not prevent model loading or
tokenization. Applications that require all pending entries to be committed can
call `llama_token_cache_flush()`.
The 5 GiB limit accounts for logical entry payloads. SQLite metadata and bounded
journal files are not included.
+26
View File
@@ -304,6 +304,26 @@ extern "C" {
ggml_backend_buffer_type_t buft; ggml_backend_buffer_type_t buft;
}; };
struct llama_token_cache_params {
// NULL selects the platform llama.cpp cache directory.
const char * path;
// Shared logical entry budget. 0 disables the cache for this process.
uint64_t capacity_bytes;
};
struct llama_token_cache_stats {
uint64_t tokenizer_hits;
uint64_t tokenizer_misses;
uint64_t text_hits;
uint64_t text_misses;
uint64_t writes;
uint64_t dropped_writes;
uint64_t evictions;
uint64_t errors;
uint64_t entries;
uint64_t bytes_used;
};
struct llama_model_params { struct llama_model_params {
// NULL-terminated list of devices to use for offloading (if NULL, all available devices are used) // NULL-terminated list of devices to use for offloading (if NULL, all available devices are used)
ggml_backend_dev_t * devices; ggml_backend_dev_t * devices;
@@ -460,6 +480,12 @@ extern "C" {
// Helpers for getting default parameters // Helpers for getting default parameters
// TODO: update API to start accepting pointers to params structs (https://github.com/ggml-org/llama.cpp/discussions/9172) // TODO: update API to start accepting pointers to params structs (https://github.com/ggml-org/llama.cpp/discussions/9172)
LLAMA_API struct llama_model_params llama_model_default_params(void); LLAMA_API struct llama_model_params llama_model_default_params(void);
LLAMA_API struct llama_token_cache_params llama_token_cache_default_params(void);
// Must be called before the first model load or tokenization operation.
LLAMA_API bool llama_token_cache_configure(struct llama_token_cache_params params);
LLAMA_API bool llama_token_cache_flush(void);
LLAMA_API bool llama_token_cache_clear(void);
LLAMA_API struct llama_token_cache_stats llama_token_cache_get_stats(void);
LLAMA_API struct llama_context_params llama_context_default_params(void); LLAMA_API struct llama_context_params llama_context_default_params(void);
LLAMA_API struct llama_sampler_chain_params llama_sampler_chain_default_params(void); LLAMA_API struct llama_sampler_chain_params llama_sampler_chain_default_params(void);
LLAMA_API struct llama_model_quantize_params llama_model_quantize_default_params(void); LLAMA_API struct llama_model_quantize_params llama_model_quantize_default_params(void);
+8
View File
@@ -37,6 +37,7 @@ add_library(llama
llama-model.cpp llama-model.cpp
llama-quant.cpp llama-quant.cpp
llama-sampler.cpp llama-sampler.cpp
llama-token-cache.cpp
llama-vocab.cpp llama-vocab.cpp
unicode-data.cpp unicode-data.cpp
unicode.cpp unicode.cpp
@@ -56,6 +57,13 @@ target_compile_features (llama PRIVATE cxx_std_17) # don't bump
target_link_libraries(llama PUBLIC ggml) target_link_libraries(llama PUBLIC ggml)
find_package(SQLite3 REQUIRED)
if (TARGET SQLite3::SQLite3)
target_link_libraries(llama PRIVATE SQLite3::SQLite3)
else()
target_link_libraries(llama PRIVATE SQLite::SQLite3)
endif()
if (BUILD_SHARED_LIBS) if (BUILD_SHARED_LIBS)
set_target_properties(llama PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(llama PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(llama PRIVATE LLAMA_BUILD) target_compile_definitions(llama PRIVATE LLAMA_BUILD)
+33
View File
@@ -1,4 +1,5 @@
#include "llama-model-loader.h" #include "llama-model-loader.h"
#include "llama-token-cache.h"
#include "ggml-alloc.h" #include "ggml-alloc.h"
#include "ggml.h" #include "ggml.h"
@@ -12,6 +13,7 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <future> #include <future>
#include <filesystem>
#include <regex> #include <regex>
static const size_t kiB = 1024; static const size_t kiB = 1024;
@@ -540,6 +542,33 @@ llama_model_loader::llama_model_loader(
} }
} }
if (!fname.empty()) {
std::error_code path_ec;
std::error_code size_ec;
std::error_code time_ec;
const auto absolute = std::filesystem::absolute(fname, path_ec);
const auto size = std::filesystem::file_size(fname, size_ec);
const auto mtime = std::filesystem::last_write_time(fname, time_ec).time_since_epoch().count();
std::string source = (path_ec ? fname : absolute.string()) + '\n' +
std::to_string(size_ec ? 0 : static_cast<uint64_t>(size)) + '\n' +
std::to_string(time_ec ? 0 : static_cast<int64_t>(mtime));
std::vector<std::string> override_keys;
override_keys.reserve(kv_overrides.size());
for (const auto & item : kv_overrides) override_keys.push_back(item.first);
std::sort(override_keys.begin(), override_keys.end());
for (const auto & key : override_keys) {
const auto & value = kv_overrides.at(key);
source += '\n' + key + ':' + std::to_string(value.tag) + ':';
switch (value.tag) {
case LLAMA_KV_OVERRIDE_TYPE_INT: source += std::to_string(value.val_i64); break;
case LLAMA_KV_OVERRIDE_TYPE_FLOAT: source += std::to_string(value.val_f64); break;
case LLAMA_KV_OVERRIDE_TYPE_BOOL: source += value.val_bool ? "1" : "0"; break;
case LLAMA_KV_OVERRIDE_TYPE_STR: source += value.val_str; break;
}
}
token_cache_source_key = "source:" + llama_token_cache_hash(source);
}
tensor_buft_overrides = param_tensor_buft_overrides_p; tensor_buft_overrides = param_tensor_buft_overrides_p;
this->use_mmap = load_mode == LLAMA_LOAD_MODE_MMAP || load_mode == LLAMA_LOAD_MODE_MMAP_MLOCK; this->use_mmap = load_mode == LLAMA_LOAD_MODE_MMAP || load_mode == LLAMA_LOAD_MODE_MMAP_MLOCK;
@@ -862,6 +891,10 @@ enum llm_arch llama_model_loader::get_arch() const {
return llm_kv.arch; return llm_kv.arch;
} }
const std::string & llama_model_loader::get_token_cache_source_key() const {
return token_cache_source_key;
}
const llama_model_loader::llama_tensor_weight * llama_model_loader::get_weight(const char * name) const { const llama_model_loader::llama_tensor_weight * llama_model_loader::get_weight(const char * name) const {
auto pos = weights_map.find(name); auto pos = weights_map.find(name);
if (pos != weights_map.end()) { if (pos != weights_map.end()) {
+3
View File
@@ -107,6 +107,7 @@ struct llama_model_loader {
std::vector<ggml_context_ptr> contexts; std::vector<ggml_context_ptr> contexts;
std::string arch_name; std::string arch_name;
std::string token_cache_source_key;
LLM_KV llm_kv = LLM_KV(LLM_ARCH_UNKNOWN); LLM_KV llm_kv = LLM_KV(LLM_ARCH_UNKNOWN);
size_t size_done = 0; size_t size_done = 0;
@@ -179,6 +180,8 @@ struct llama_model_loader {
enum llm_arch get_arch() const; enum llm_arch get_arch() const;
const std::string & get_token_cache_source_key() const;
void configure_longhaul(uint64_t cache_bytes, uint32_t n_expert, uint32_t n_layer); void configure_longhaul(uint64_t cache_bytes, uint32_t n_expert, uint32_t n_layer);
const llama_tensor_weight * get_weight(const char * name) const; const llama_tensor_weight * get_weight(const char * name) const;
+514
View File
@@ -0,0 +1,514 @@
#include "llama-token-cache.h"
#include "llama-impl.h"
#include <sqlite3.h>
#include <algorithm>
#include <array>
#include <atomic>
#include <condition_variable>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <filesystem>
#include <limits>
#include <mutex>
#include <thread>
#if defined(_WIN32)
# include <windows.h>
#else
# include <sys/stat.h>
# include <unistd.h>
#endif
namespace fs = std::filesystem;
namespace {
constexpr uint64_t DEFAULT_CAPACITY = 5ull * 1024 * 1024 * 1024;
constexpr uint64_t MAX_QUEUE_BYTES = 64ull * 1024 * 1024;
constexpr int SCHEMA_VERSION = 1;
struct sha256 {
uint32_t h[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
};
uint8_t block[64] = {};
uint64_t bits = 0;
size_t used = 0;
static uint32_t rotr(uint32_t x, uint32_t n) { return (x >> n) | (x << (32 - n)); }
void compress(const uint8_t * p) {
static const uint32_t k[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2,
};
uint32_t w[64];
for (int i = 0; i < 16; ++i) {
w[i] = uint32_t(p[4*i]) << 24 | uint32_t(p[4*i+1]) << 16 | uint32_t(p[4*i+2]) << 8 | p[4*i+3];
}
for (int i = 16; i < 64; ++i) {
const uint32_t s0 = rotr(w[i-15], 7) ^ rotr(w[i-15], 18) ^ (w[i-15] >> 3);
const uint32_t s1 = rotr(w[i-2], 17) ^ rotr(w[i-2], 19) ^ (w[i-2] >> 10);
w[i] = w[i-16] + s0 + w[i-7] + s1;
}
uint32_t a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7];
for (int i = 0; i < 64; ++i) {
const uint32_t s1 = rotr(e,6)^rotr(e,11)^rotr(e,25);
const uint32_t ch = (e&f)^((~e)&g);
const uint32_t t1 = hh+s1+ch+k[i]+w[i];
const uint32_t s0 = rotr(a,2)^rotr(a,13)^rotr(a,22);
const uint32_t maj = (a&b)^(a&c)^(b&c);
const uint32_t t2 = s0+maj;
hh=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2;
}
h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh;
}
void update(const void * data, size_t n) {
const auto * p = static_cast<const uint8_t *>(data);
bits += uint64_t(n) * 8;
while (n) {
const size_t take = std::min(n, sizeof(block) - used);
memcpy(block + used, p, take);
used += take; p += take; n -= take;
if (used == sizeof(block)) { compress(block); used = 0; }
}
}
std::array<uint8_t, 32> finish() {
const uint64_t original_bits = bits;
const uint8_t one = 0x80;
update(&one, 1);
const uint8_t zero = 0;
while (used != 56) update(&zero, 1);
uint8_t len[8];
for (int i = 0; i < 8; ++i) len[7-i] = uint8_t(original_bits >> (8*i));
update(len, 8);
std::array<uint8_t, 32> out;
for (int i = 0; i < 8; ++i) {
out[4*i]=uint8_t(h[i]>>24); out[4*i+1]=uint8_t(h[i]>>16);
out[4*i+2]=uint8_t(h[i]>>8); out[4*i+3]=uint8_t(h[i]);
}
return out;
}
};
struct queued_write {
llama_token_cache_kind kind;
std::string key;
std::vector<uint8_t> payload;
};
struct cache_counters {
std::atomic<uint64_t> tokenizer_hits{0};
std::atomic<uint64_t> tokenizer_misses{0};
std::atomic<uint64_t> text_hits{0};
std::atomic<uint64_t> text_misses{0};
std::atomic<uint64_t> writes{0};
std::atomic<uint64_t> dropped_writes{0};
std::atomic<uint64_t> evictions{0};
std::atomic<uint64_t> errors{0};
};
class token_cache {
public:
token_cache() {
params.capacity_bytes = DEFAULT_CAPACITY;
worker = std::thread([this] { run(); });
}
~token_cache() {
flush();
{
std::lock_guard<std::mutex> lock(mutex);
stopping = true;
}
ready.notify_all();
if (worker.joinable()) worker.join();
close();
}
bool configure(llama_token_cache_params value) {
std::lock_guard<std::mutex> lock(mutex);
if (initialized) return false;
params.capacity_bytes = value.capacity_bytes;
configured_path = value.path ? value.path : "";
return true;
}
bool lookup(llama_token_cache_kind kind, const std::string & key, std::vector<uint8_t> & out) {
if (!ensure_open()) return false;
std::lock_guard<std::mutex> db_lock(db_mutex);
sqlite3_stmt * stmt = nullptr;
const char * sql = "SELECT payload FROM entries WHERE kind=?1 AND key=?2";
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) return db_error();
sqlite3_bind_int(stmt, 1, kind);
sqlite3_bind_text(stmt, 2, key.data(), int(key.size()), SQLITE_TRANSIENT);
bool found = false;
if (sqlite3_step(stmt) == SQLITE_ROW) {
const int n = sqlite3_column_bytes(stmt, 0);
const void * p = sqlite3_column_blob(stmt, 0);
if (n >= 0 && (n == 0 || p != nullptr)) {
out.assign(static_cast<const uint8_t *>(p), static_cast<const uint8_t *>(p) + n);
found = true;
}
}
sqlite3_finalize(stmt);
if (found) {
sqlite3_stmt * touch = nullptr;
if (sqlite3_prepare_v2(db, "UPDATE entries SET last_access=?1 WHERE kind=?2 AND key=?3", -1, &touch, nullptr) == SQLITE_OK) {
sqlite3_bind_int64(touch, 1, now_tick());
sqlite3_bind_int(touch, 2, kind);
sqlite3_bind_text(touch, 3, key.data(), int(key.size()), SQLITE_TRANSIENT);
sqlite3_step(touch);
}
sqlite3_finalize(touch);
if (kind == LLAMA_TOKEN_CACHE_KIND_TEXT) ++stats.text_hits;
} else if (kind == LLAMA_TOKEN_CACHE_KIND_TEXT) {
++stats.text_misses;
}
return found;
}
void store(llama_token_cache_kind kind, const std::string & key, const std::vector<uint8_t> & payload, bool sync) {
if (params.capacity_bytes == 0 || payload.size() > params.capacity_bytes ||
payload.size() > static_cast<size_t>(std::numeric_limits<int>::max())) return;
if (sync) {
write_one({kind, key, payload});
return;
}
std::lock_guard<std::mutex> lock(mutex);
if (queue_bytes + payload.size() > MAX_QUEUE_BYTES) {
++stats.dropped_writes;
return;
}
queue.push_back({kind, key, payload});
queue_bytes += payload.size();
ready.notify_one();
}
void flush() {
std::unique_lock<std::mutex> lock(mutex);
drained.wait(lock, [&] { return queue.empty() && !writing; });
if (db) {
lock.unlock();
std::lock_guard<std::mutex> db_lock(db_mutex);
sqlite3_wal_checkpoint_v2(db, nullptr, SQLITE_CHECKPOINT_PASSIVE, nullptr, nullptr);
}
}
bool clear_all() {
flush();
if (!ensure_open()) return params.capacity_bytes == 0;
std::lock_guard<std::mutex> db_lock(db_mutex);
if (sqlite3_exec(db, "DELETE FROM entries; PRAGMA incremental_vacuum;", nullptr, nullptr, nullptr) != SQLITE_OK) return db_error();
return true;
}
llama_token_cache_stats get_stats() {
llama_token_cache_stats result = {
stats.tokenizer_hits.load(),
stats.tokenizer_misses.load(),
stats.text_hits.load(),
stats.text_misses.load(),
stats.writes.load(),
stats.dropped_writes.load(),
stats.evictions.load(),
stats.errors.load(),
0,
0,
};
if (ensure_open()) {
std::lock_guard<std::mutex> db_lock(db_mutex);
sqlite3_stmt * stmt = nullptr;
if (sqlite3_prepare_v2(db, "SELECT COUNT(*), COALESCE(SUM(size),0) FROM entries", -1, &stmt, nullptr) == SQLITE_OK &&
sqlite3_step(stmt) == SQLITE_ROW) {
result.entries = sqlite3_column_int64(stmt, 0);
result.bytes_used = sqlite3_column_int64(stmt, 1);
}
sqlite3_finalize(stmt);
}
return result;
}
void note_tokenizer(bool hit) {
if (hit) ++stats.tokenizer_hits; else ++stats.tokenizer_misses;
}
private:
static int64_t now_tick() {
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
std::string default_path() {
if (const char * p = std::getenv("LLAMA_CACHE"); p && *p) return (fs::path(p) / "token-cache.sqlite3").string();
#if defined(_WIN32)
if (const char * p = std::getenv("LOCALAPPDATA"); p && *p) return (fs::path(p) / "llama.cpp" / "token-cache.sqlite3").string();
#elif defined(__APPLE__)
if (const char * p = std::getenv("HOME"); p && *p) return (fs::path(p) / "Library" / "Caches" / "llama.cpp" / "token-cache.sqlite3").string();
#else
if (const char * p = std::getenv("XDG_CACHE_HOME"); p && *p) return (fs::path(p) / "llama.cpp" / "token-cache.sqlite3").string();
if (const char * p = std::getenv("HOME"); p && *p) return (fs::path(p) / ".cache" / "llama.cpp" / "token-cache.sqlite3").string();
#endif
return {};
}
bool ensure_open() {
std::lock_guard<std::mutex> init_lock(init_mutex);
if (initialized) return db != nullptr;
initialized = true;
if (params.capacity_bytes == 0) return false;
path = configured_path.empty() ? default_path() : configured_path;
if (path.empty()) return false;
std::error_code ec;
fs::create_directories(fs::path(path).parent_path(), ec);
if (ec) return false;
#if !defined(_WIN32)
chmod(fs::path(path).parent_path().c_str(), 0700);
#endif
if (sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr) != SQLITE_OK) {
warn_once("failed to open token cache database");
close();
return false;
}
#if !defined(_WIN32)
chmod(path.c_str(), 0600);
#endif
sqlite3_busy_timeout(db, 5000);
const char * schema =
"PRAGMA journal_mode=WAL;"
"PRAGMA synchronous=NORMAL;"
"PRAGMA auto_vacuum=INCREMENTAL;"
"CREATE TABLE IF NOT EXISTS meta(key TEXT PRIMARY KEY,value INTEGER NOT NULL);"
"CREATE TABLE IF NOT EXISTS entries("
" kind INTEGER NOT NULL,key TEXT NOT NULL,payload BLOB NOT NULL,size INTEGER NOT NULL,last_access INTEGER NOT NULL,"
" PRIMARY KEY(kind,key));"
"CREATE INDEX IF NOT EXISTS entries_lru ON entries(last_access);";
if (sqlite3_exec(db, schema, nullptr, nullptr, nullptr) != SQLITE_OK) {
warn_once("failed to initialize token cache database");
close();
return false;
}
sqlite3_stmt * stmt = nullptr;
sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO meta(key,value) VALUES('schema',?1)", -1, &stmt, nullptr);
sqlite3_bind_int(stmt, 1, SCHEMA_VERSION);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO meta(key,value) VALUES('capacity',?1)", -1, &stmt, nullptr);
sqlite3_bind_int64(stmt, 1, static_cast<sqlite3_int64>(params.capacity_bytes));
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_exec(db, "BEGIN IMMEDIATE", nullptr, nullptr, nullptr);
while (true) {
sqlite3_prepare_v2(db, "SELECT COALESCE(SUM(size),0) FROM entries", -1, &stmt, nullptr);
uint64_t total = 0;
if (sqlite3_step(stmt) == SQLITE_ROW) total = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
if (total <= params.capacity_bytes) break;
if (sqlite3_exec(db,
"DELETE FROM entries WHERE rowid=(SELECT rowid FROM entries ORDER BY last_access LIMIT 1)",
nullptr, nullptr, nullptr) != SQLITE_OK) {
break;
}
++stats.evictions;
}
sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
return true;
}
void run() {
while (true) {
queued_write item;
{
std::unique_lock<std::mutex> lock(mutex);
ready.wait(lock, [&] { return stopping || !queue.empty(); });
if (stopping && queue.empty()) return;
item = std::move(queue.front());
queue.pop_front();
queue_bytes -= item.payload.size();
writing = true;
}
write_one(item);
{
std::lock_guard<std::mutex> lock(mutex);
writing = false;
if (queue.empty()) drained.notify_all();
}
}
}
void write_one(const queued_write & item) {
if (!ensure_open()) return;
std::lock_guard<std::mutex> db_lock(db_mutex);
if (sqlite3_exec(db, "BEGIN IMMEDIATE", nullptr, nullptr, nullptr) != SQLITE_OK) { db_error(); return; }
uint64_t capacity = params.capacity_bytes;
sqlite3_stmt * capacity_stmt = nullptr;
if (sqlite3_prepare_v2(db, "SELECT value FROM meta WHERE key='capacity'", -1, &capacity_stmt, nullptr) == SQLITE_OK &&
sqlite3_step(capacity_stmt) == SQLITE_ROW) {
capacity = static_cast<uint64_t>(sqlite3_column_int64(capacity_stmt, 0));
}
sqlite3_finalize(capacity_stmt);
if (item.payload.size() > capacity) {
sqlite3_exec(db, "ROLLBACK", nullptr, nullptr, nullptr);
return;
}
sqlite3_stmt * existing = nullptr;
uint64_t old_size = 0;
sqlite3_prepare_v2(db, "SELECT size FROM entries WHERE kind=?1 AND key=?2", -1, &existing, nullptr);
sqlite3_bind_int(existing, 1, item.kind);
sqlite3_bind_text(existing, 2, item.key.data(), int(item.key.size()), SQLITE_TRANSIENT);
if (sqlite3_step(existing) == SQLITE_ROW) old_size = sqlite3_column_int64(existing, 0);
sqlite3_finalize(existing);
sqlite3_stmt * total_stmt = nullptr;
uint64_t total = 0;
sqlite3_prepare_v2(db, "SELECT COALESCE(SUM(size),0) FROM entries", -1, &total_stmt, nullptr);
if (sqlite3_step(total_stmt) == SQLITE_ROW) total = sqlite3_column_int64(total_stmt, 0);
sqlite3_finalize(total_stmt);
total -= std::min(total, old_size);
while (total + item.payload.size() > capacity) {
sqlite3_stmt * victim = nullptr;
sqlite3_prepare_v2(db, "SELECT kind,key,size FROM entries WHERE NOT(kind=?1 AND key=?2) ORDER BY last_access LIMIT 1", -1, &victim, nullptr);
sqlite3_bind_int(victim, 1, item.kind);
sqlite3_bind_text(victim, 2, item.key.data(), int(item.key.size()), SQLITE_TRANSIENT);
if (sqlite3_step(victim) != SQLITE_ROW) { sqlite3_finalize(victim); break; }
const int victim_kind = sqlite3_column_int(victim, 0);
const std::string victim_key(reinterpret_cast<const char *>(sqlite3_column_text(victim, 1)));
const uint64_t victim_size = sqlite3_column_int64(victim, 2);
sqlite3_finalize(victim);
sqlite3_stmt * del = nullptr;
sqlite3_prepare_v2(db, "DELETE FROM entries WHERE kind=?1 AND key=?2", -1, &del, nullptr);
sqlite3_bind_int(del, 1, victim_kind);
sqlite3_bind_text(del, 2, victim_key.data(), int(victim_key.size()), SQLITE_TRANSIENT);
sqlite3_step(del);
sqlite3_finalize(del);
total -= std::min(total, victim_size);
++stats.evictions;
}
sqlite3_stmt * put = nullptr;
sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO entries(kind,key,payload,size,last_access) VALUES(?1,?2,?3,?4,?5)", -1, &put, nullptr);
sqlite3_bind_int(put, 1, item.kind);
sqlite3_bind_text(put, 2, item.key.data(), int(item.key.size()), SQLITE_TRANSIENT);
sqlite3_bind_blob(put, 3, item.payload.data(), int(item.payload.size()), SQLITE_TRANSIENT);
sqlite3_bind_int64(put, 4, item.payload.size());
sqlite3_bind_int64(put, 5, now_tick());
const int rc = sqlite3_step(put);
sqlite3_finalize(put);
if (rc == SQLITE_DONE) {
sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
++stats.writes;
} else {
sqlite3_exec(db, "ROLLBACK", nullptr, nullptr, nullptr);
db_error();
}
}
bool db_error() {
++stats.errors;
warn_once("token cache database operation failed; treating it as a cache miss");
return false;
}
void warn_once(const char * message) {
bool expected = false;
if (warned.compare_exchange_strong(expected, true)) {
LLAMA_LOG_WARN("%s\n", message);
}
}
void close() {
std::lock_guard<std::mutex> db_lock(db_mutex);
if (db) sqlite3_close(db);
db = nullptr;
}
llama_token_cache_params params = {nullptr, DEFAULT_CAPACITY};
std::string configured_path;
std::string path;
sqlite3 * db = nullptr;
std::mutex init_mutex;
std::mutex db_mutex;
std::mutex mutex;
std::condition_variable ready;
std::condition_variable drained;
std::deque<queued_write> queue;
uint64_t queue_bytes = 0;
bool initialized = false;
bool stopping = false;
bool writing = false;
std::atomic<bool> warned{false};
std::thread worker;
cache_counters stats;
};
token_cache & cache() {
static token_cache instance;
return instance;
}
} // namespace
std::string llama_token_cache_hash(const void * data, size_t size) {
sha256 hash;
hash.update(data, size);
const auto bytes = hash.finish();
static const char hex[] = "0123456789abcdef";
std::string result(64, '0');
for (size_t i = 0; i < bytes.size(); ++i) {
result[2*i] = hex[bytes[i] >> 4];
result[2*i+1] = hex[bytes[i] & 15];
}
return result;
}
std::string llama_token_cache_hash(const std::string & data) {
return llama_token_cache_hash(data.data(), data.size());
}
bool llama_token_cache_lookup(llama_token_cache_kind kind, const std::string & key, std::vector<uint8_t> & payload) {
return cache().lookup(kind, key, payload);
}
void llama_token_cache_store(llama_token_cache_kind kind, const std::string & key, const std::vector<uint8_t> & payload, bool synchronous) {
cache().store(kind, key, payload, synchronous);
}
void llama_token_cache_note_tokenizer_hit(bool hit) {
cache().note_tokenizer(hit);
}
llama_token_cache_params llama_token_cache_default_params() {
return {nullptr, DEFAULT_CAPACITY};
}
bool llama_token_cache_configure(llama_token_cache_params params) {
return cache().configure(params);
}
bool llama_token_cache_flush() {
cache().flush();
return true;
}
bool llama_token_cache_clear() {
return cache().clear_all();
}
llama_token_cache_stats llama_token_cache_get_stats() {
return cache().get_stats();
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "llama.h"
#include <cstdint>
#include <string>
#include <vector>
enum llama_token_cache_kind : int32_t {
LLAMA_TOKEN_CACHE_KIND_TOKENIZER = 1,
LLAMA_TOKEN_CACHE_KIND_TEXT = 2,
};
std::string llama_token_cache_hash(const void * data, size_t size);
std::string llama_token_cache_hash(const std::string & data);
bool llama_token_cache_lookup(
llama_token_cache_kind kind,
const std::string & key,
std::vector<uint8_t> & payload);
void llama_token_cache_store(
llama_token_cache_kind kind,
const std::string & key,
const std::vector<uint8_t> & payload,
bool synchronous = false);
void llama_token_cache_note_tokenizer_hit(bool hit);
+276 -1
View File
@@ -1,4 +1,5 @@
#include "llama-vocab.h" #include "llama-vocab.h"
#include "llama-token-cache.h"
#include "ggml.h" #include "ggml.h"
#include "gguf.h" #include "gguf.h"
@@ -1841,6 +1842,7 @@ struct llama_vocab::impl {
std::vector<llama_token> suppress_tokens; std::vector<llama_token> suppress_tokens;
std::unique_ptr<llm_tokenizer> tokenizer; std::unique_ptr<llm_tokenizer> tokenizer;
std::string token_cache_id;
std::vector<char> precompiled_charsmap; std::vector<char> precompiled_charsmap;
@@ -1851,6 +1853,9 @@ struct llama_vocab::impl {
void load(llama_model_loader & ml, const LLM_KV & kv); void load(llama_model_loader & ml, const LLM_KV & kv);
bool load_cache_snapshot(const std::vector<uint8_t> & snapshot);
void reset_cache_snapshot_state();
enum llama_vocab_type get_type() const; enum llama_vocab_type get_type() const;
std::string type_name() const; std::string type_name() const;
@@ -1881,6 +1886,11 @@ struct llama_vocab::impl {
bool add_special, bool add_special,
bool parse_special = false) const; bool parse_special = false) const;
std::vector<llama_token> tokenize_uncached(
const std::string & raw_text,
bool add_special,
bool parse_special = false) const;
int32_t tokenize( int32_t tokenize(
const char * text, const char * text,
int32_t text_len, int32_t text_len,
@@ -1921,6 +1931,25 @@ private:
void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) { void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
struct gguf_context * ctx = ml.metadata; struct gguf_context * ctx = ml.metadata;
const std::string & source_cache_key = ml.get_token_cache_source_key();
if (!source_cache_key.empty()) {
std::vector<uint8_t> snapshot;
bool found = llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, source_cache_key, snapshot);
if (found && snapshot.size() == 65 && snapshot[0] == 'A') {
const std::string content_key(reinterpret_cast<const char *>(snapshot.data() + 1), 64);
snapshot.clear();
found = llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, content_key, snapshot);
}
if (found &&
load_cache_snapshot(snapshot)) {
token_cache_id = llama_token_cache_hash(snapshot.data(), snapshot.size());
llama_token_cache_note_tokenizer_hit(true);
return;
}
reset_cache_snapshot_state();
llama_token_cache_note_tokenizer_hit(false);
}
// determine vocab type // determine vocab type
{ {
ml.get_key(LLM_KV_TOKENIZER_MODEL, tokenizer_model); ml.get_key(LLM_KV_TOKENIZER_MODEL, tokenizer_model);
@@ -3033,6 +3062,206 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
} }
} }
} }
// Persist a versioned canonical snapshot of the effective tokenizer state.
// The snapshot also serves as the namespace for text-to-token cache entries.
{
std::vector<uint8_t> snapshot;
auto put = [&snapshot](const auto & value) {
const auto * p = reinterpret_cast<const uint8_t *>(&value);
snapshot.insert(snapshot.end(), p, p + sizeof(value));
};
auto put_string = [&snapshot, &put](const std::string & value) {
const uint64_t n = value.size();
put(n);
snapshot.insert(snapshot.end(), value.begin(), value.end());
};
const uint32_t version = 2;
put(version);
put(type);
put(pre_type);
put(n_token_types);
put(max_token_len);
put_string(tokenizer_model);
put_string(tokenizer_pre);
put(special_bos_id); put(special_eos_id); put(special_eot_id); put(special_eom_id);
put(special_unk_id); put(special_sep_id); put(special_pad_id); put(special_mask_id);
put(special_fim_pre_id); put(special_fim_suf_id); put(special_fim_mid_id);
put(special_fim_pad_id); put(special_fim_rep_id); put(special_fim_sep_id);
put(linefeed_id);
put(add_space_prefix); put(add_bos); put(add_eos); put(add_sep); put(ignore_merges);
put(clean_spaces); put(remove_extra_whitespaces); put(escape_whitespaces); put(treat_whitespace_as_suffix);
put(normalizer_opts.lowercase);
put(normalizer_opts.strip_accents);
const uint64_t n_tokens = id_to_token.size();
put(n_tokens);
for (const auto & item : id_to_token) {
put_string(item.text);
put(item.score);
put(item.attr);
}
std::vector<std::pair<std::string, int>> merges;
merges.reserve(bpe_ranks.size());
for (const auto & item : bpe_ranks) {
merges.emplace_back(item.first.first + '\0' + item.first.second, item.second);
}
std::sort(merges.begin(), merges.end());
const uint64_t n_merges = merges.size();
put(n_merges);
for (const auto & item : merges) {
put_string(item.first);
put(item.second);
}
const uint64_t charsmap_size = precompiled_charsmap.size();
put(charsmap_size);
snapshot.insert(snapshot.end(), precompiled_charsmap.begin(), precompiled_charsmap.end());
const uint64_t n_suppress = suppress_tokens.size();
put(n_suppress);
for (const auto token : suppress_tokens) put(token);
const uint64_t n_special = cache_special_tokens.size();
put(n_special);
for (const auto token : cache_special_tokens) put(token);
const uint64_t n_pieces = cache_token_to_piece.size();
put(n_pieces);
for (const auto & piece : cache_token_to_piece) put_string(piece);
const uint64_t n_eog = special_eog_ids.size();
put(n_eog);
for (const auto token : special_eog_ids) put(token);
std::vector<std::pair<std::string, llama_token>> text_ids(token_to_id.begin(), token_to_id.end());
std::sort(text_ids.begin(), text_ids.end());
const uint64_t n_text_ids = text_ids.size();
put(n_text_ids);
for (const auto & item : text_ids) {
put_string(item.first);
put(item.second);
}
token_cache_id = llama_token_cache_hash(snapshot.data(), snapshot.size());
std::vector<uint8_t> cached;
const bool hit = llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, token_cache_id, cached);
if (!hit) {
llama_token_cache_store(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, token_cache_id, snapshot, true);
}
if (!source_cache_key.empty()) {
std::vector<uint8_t> alias(1 + token_cache_id.size());
alias[0] = 'A';
memcpy(alias.data() + 1, token_cache_id.data(), token_cache_id.size());
llama_token_cache_store(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, source_cache_key, alias, true);
}
}
}
void llama_vocab::impl::reset_cache_snapshot_state() {
n_token_types = 0;
tokenizer_model.clear();
tokenizer_pre.clear();
type = LLAMA_VOCAB_TYPE_SPM;
pre_type = LLAMA_VOCAB_PRE_TYPE_DEFAULT;
max_token_len = 0;
special_bos_id = 1; special_eos_id = 2; special_eot_id = LLAMA_TOKEN_NULL; special_eom_id = LLAMA_TOKEN_NULL;
special_unk_id = 0; special_sep_id = LLAMA_TOKEN_NULL; special_pad_id = LLAMA_TOKEN_NULL; special_mask_id = LLAMA_TOKEN_NULL;
linefeed_id = 13;
special_fim_pre_id = special_fim_suf_id = special_fim_mid_id = LLAMA_TOKEN_NULL;
special_fim_pad_id = special_fim_rep_id = special_fim_sep_id = LLAMA_TOKEN_NULL;
add_space_prefix = false; add_bos = false; add_eos = false; add_sep = false;
ignore_merges = false; clean_spaces = false; remove_extra_whitespaces = false;
escape_whitespaces = true; treat_whitespace_as_suffix = false;
normalizer_opts = {};
token_to_id.clear();
id_to_token.clear();
cache_special_tokens.clear();
cache_token_to_piece.clear();
bpe_ranks.clear();
special_eog_ids.clear();
suppress_tokens.clear();
tokenizer.reset();
precompiled_charsmap.clear();
token_cache_id.clear();
}
bool llama_vocab::impl::load_cache_snapshot(const std::vector<uint8_t> & snapshot) {
size_t offset = 0;
auto get = [&snapshot, &offset](auto & value) {
if (sizeof(value) > snapshot.size() - std::min(offset, snapshot.size())) return false;
memcpy(&value, snapshot.data() + offset, sizeof(value));
offset += sizeof(value);
return true;
};
auto get_string = [&snapshot, &offset, &get](std::string & value) {
uint64_t n = 0;
if (!get(n) || n > snapshot.size() - std::min(offset, snapshot.size())) return false;
value.assign(reinterpret_cast<const char *>(snapshot.data() + offset), static_cast<size_t>(n));
offset += static_cast<size_t>(n);
return true;
};
auto get_count = [&get](uint64_t & n) {
return get(n) && n <= 2000000;
};
uint32_t version = 0;
if (!get(version) || version != 2 ||
!get(type) || !get(pre_type) || !get(n_token_types) || !get(max_token_len) ||
!get_string(tokenizer_model) || !get_string(tokenizer_pre) ||
!get(special_bos_id) || !get(special_eos_id) || !get(special_eot_id) || !get(special_eom_id) ||
!get(special_unk_id) || !get(special_sep_id) || !get(special_pad_id) || !get(special_mask_id) ||
!get(special_fim_pre_id) || !get(special_fim_suf_id) || !get(special_fim_mid_id) ||
!get(special_fim_pad_id) || !get(special_fim_rep_id) || !get(special_fim_sep_id) ||
!get(linefeed_id) ||
!get(add_space_prefix) || !get(add_bos) || !get(add_eos) || !get(add_sep) || !get(ignore_merges) ||
!get(clean_spaces) || !get(remove_extra_whitespaces) || !get(escape_whitespaces) || !get(treat_whitespace_as_suffix) ||
!get(normalizer_opts.lowercase) || !get(normalizer_opts.strip_accents)) {
return false;
}
if (type <= LLAMA_VOCAB_TYPE_NONE || type > LLAMA_VOCAB_TYPE_PLAMO2) return false;
uint64_t count = 0;
if (!get_count(count)) return false;
id_to_token.resize(count);
for (auto & item : id_to_token) {
if (!get_string(item.text) || !get(item.score) || !get(item.attr)) return false;
}
if (!get_count(count)) return false;
for (uint64_t i = 0; i < count; ++i) {
std::string joined;
int rank = 0;
if (!get_string(joined) || !get(rank)) return false;
const auto split = joined.find('\0');
if (split == std::string::npos) return false;
bpe_ranks.emplace(std::make_pair(joined.substr(0, split), joined.substr(split + 1)), rank);
}
uint64_t charsmap_size = 0;
if (!get_count(charsmap_size) || charsmap_size > snapshot.size() - std::min(offset, snapshot.size())) return false;
precompiled_charsmap.assign(snapshot.begin() + offset, snapshot.begin() + offset + charsmap_size);
offset += charsmap_size;
if (!get_count(count)) return false;
suppress_tokens.resize(count);
for (auto & token : suppress_tokens) if (!get(token)) return false;
if (!get_count(count)) return false;
cache_special_tokens.resize(count);
for (auto & token : cache_special_tokens) if (!get(token)) return false;
if (!get_count(count)) return false;
cache_token_to_piece.resize(count);
for (auto & piece : cache_token_to_piece) if (!get_string(piece)) return false;
if (!get_count(count)) return false;
for (uint64_t i = 0; i < count; ++i) {
llama_token token;
if (!get(token)) return false;
special_eog_ids.insert(token);
}
if (!get_count(count)) return false;
for (uint64_t i = 0; i < count; ++i) {
std::string text;
llama_token token;
if (!get_string(text) || !get(token) || token < 0 || static_cast<size_t>(token) >= id_to_token.size()) return false;
token_to_id.emplace(std::move(text), token);
}
if (offset != snapshot.size() || cache_token_to_piece.size() != id_to_token.size()) return false;
init_tokenizer(type);
return true;
} }
enum llama_vocab_type llama_vocab::impl::get_type() const { enum llama_vocab_type llama_vocab::impl::get_type() const {
@@ -3310,7 +3539,7 @@ static std::string llama_decode_text(const std::string & text) {
return decoded_text; return decoded_text;
} }
std::vector<llama_token> llama_vocab::impl::tokenize( std::vector<llama_token> llama_vocab::impl::tokenize_uncached(
const std::string & raw_text, const std::string & raw_text,
bool add_special, bool add_special,
bool parse_special) const { bool parse_special) const {
@@ -3516,6 +3745,52 @@ std::vector<llama_token> llama_vocab::impl::tokenize(
return output; return output;
} }
std::vector<llama_token> llama_vocab::impl::tokenize(
const std::string & raw_text,
bool add_special,
bool parse_special) const {
if (raw_text.empty() || token_cache_id.empty()) {
return tokenize_uncached(raw_text, add_special, parse_special);
}
std::string key_input;
key_input.reserve(token_cache_id.size() + raw_text.size() + 3);
key_input += token_cache_id;
key_input.push_back('\0');
key_input.push_back(add_special ? '\1' : '\0');
key_input.push_back(parse_special ? '\1' : '\0');
key_input += raw_text;
const std::string key = llama_token_cache_hash(key_input);
std::vector<uint8_t> payload;
if (llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TEXT, key, payload) &&
payload.size() >= sizeof(uint32_t) &&
(payload.size() - sizeof(uint32_t)) % sizeof(llama_token) == 0) {
uint32_t count = 0;
memcpy(&count, payload.data(), sizeof(count));
const size_t available = (payload.size() - sizeof(count)) / sizeof(llama_token);
if (count == available && count < static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
std::vector<llama_token> result(count);
if (count) memcpy(result.data(), payload.data() + sizeof(count), count * sizeof(llama_token));
if (std::all_of(result.begin(), result.end(), [&](llama_token token) {
return token >= 0 && static_cast<size_t>(token) < id_to_token.size();
})) {
return result;
}
}
}
auto result = tokenize_uncached(raw_text, add_special, parse_special);
if (result.size() < static_cast<size_t>(std::numeric_limits<uint32_t>::max())) {
const uint32_t count = result.size();
payload.resize(sizeof(count) + result.size() * sizeof(llama_token));
memcpy(payload.data(), &count, sizeof(count));
if (!result.empty()) memcpy(payload.data() + sizeof(count), result.data(), result.size() * sizeof(llama_token));
llama_token_cache_store(LLAMA_TOKEN_CACHE_KIND_TEXT, key, payload);
}
return result;
}
int32_t llama_vocab::impl::token_to_piece(llama_token token, char * buf, int32_t length, int32_t lstrip, bool special) const { int32_t llama_vocab::impl::token_to_piece(llama_token token, char * buf, int32_t length, int32_t lstrip, bool special) const {
// ref: https://github.com/ggml-org/llama.cpp/pull/7587#discussion_r1620983843 // ref: https://github.com/ggml-org/llama.cpp/pull/7587#discussion_r1620983843
static const int attr_special = LLAMA_TOKEN_ATTR_UNKNOWN | LLAMA_TOKEN_ATTR_CONTROL; static const int attr_special = LLAMA_TOKEN_ATTR_UNKNOWN | LLAMA_TOKEN_ATTR_CONTROL;
+2
View File
@@ -259,6 +259,8 @@ set_tests_properties(test-thread-safety PROPERTIES FIXTURES_REQUIRED test-downlo
llama_build_and_test(test-arg-parser.cpp) llama_build_and_test(test-arg-parser.cpp)
llama_build_and_test(test-longhaul.cpp) llama_build_and_test(test-longhaul.cpp)
llama_build_and_test(test-token-cache.cpp)
target_include_directories(test-token-cache PRIVATE ${PROJECT_SOURCE_DIR}/src)
if (NOT LLAMA_SANITIZE_ADDRESS AND NOT GGML_SCHED_NO_REALLOC) if (NOT LLAMA_SANITIZE_ADDRESS AND NOT GGML_SCHED_NO_REALLOC)
# TODO: repair known memory leaks # TODO: repair known memory leaks
+8
View File
@@ -160,6 +160,14 @@ static void test(void) {
assert(params.load_mode == LLAMA_LOAD_MODE_LONGHAUL); assert(params.load_mode == LLAMA_LOAD_MODE_LONGHAUL);
assert(params.longhaul_cache_bytes == 2ULL * 1024 * 1024 * 1024); assert(params.longhaul_cache_bytes == 2ULL * 1024 * 1024 * 1024);
argv = {"binary_name", "--token-cache-size", "256", "--token-cache-dir", "/tmp/llama-token-cache-test.sqlite3"};
assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
assert(params.token_cache_size_mib == 256);
assert(params.token_cache_path == "/tmp/llama-token-cache-test.sqlite3");
argv = {"binary_name", "--token-cache-size", "-1"};
assert(false == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
// multi-value args (CSV) // multi-value args (CSV)
argv = {"binary_name", "--lora", "file1.gguf,\"file2,2.gguf\",\"file3\"\"3\"\".gguf\",file4\".gguf"}; argv = {"binary_name", "--lora", "file1.gguf,\"file2,2.gguf\",\"file3\"\"3\"\".gguf\",file4\".gguf"};
assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON)); assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
+69
View File
@@ -0,0 +1,69 @@
#include "llama.h"
#include "../src/llama-token-cache.h"
#include <chrono>
#include <filesystem>
#include <string>
#include <vector>
#undef NDEBUG
#include <cassert>
int main() {
namespace fs = std::filesystem;
const auto defaults = llama_token_cache_default_params();
assert(defaults.capacity_bytes == 5ull * 1024 * 1024 * 1024);
const fs::path root = fs::temp_directory_path() /
("llama-token-cache-test-" + std::to_string(
std::chrono::high_resolution_clock::now().time_since_epoch().count()));
const fs::path db = root / "cache.sqlite3";
llama_token_cache_params params = {
nullptr,
128,
};
// configure copies the path; it must not retain the temporary c_str().
const std::string db_string = db.string();
params.path = db_string.c_str();
assert(llama_token_cache_configure(params));
assert(llama_token_cache_clear());
assert(llama_token_cache_hash("abc") ==
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
const std::vector<uint8_t> first(80, 0x11);
const std::vector<uint8_t> second(80, 0x22);
std::vector<uint8_t> loaded;
llama_token_cache_store(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, "first", first, true);
assert(llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, "first", loaded));
assert(loaded == first);
llama_token_cache_store(LLAMA_TOKEN_CACHE_KIND_TEXT, "second", second, true);
loaded.clear();
assert(!llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TOKENIZER, "first", loaded));
assert(llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TEXT, "second", loaded));
assert(loaded == second);
const std::vector<uint8_t> async_payload(16, 0x33);
llama_token_cache_store(LLAMA_TOKEN_CACHE_KIND_TEXT, "async", async_payload);
assert(llama_token_cache_flush());
loaded.clear();
assert(llama_token_cache_lookup(LLAMA_TOKEN_CACHE_KIND_TEXT, "async", loaded));
assert(loaded == async_payload);
const auto stats = llama_token_cache_get_stats();
assert(stats.entries == 2);
assert(stats.bytes_used == second.size() + async_payload.size());
assert(stats.evictions >= 1);
assert(stats.writes >= 3);
assert(llama_token_cache_clear());
assert(llama_token_cache_get_stats().entries == 0);
std::error_code ec;
fs::remove_all(root, ec);
return 0;
}
+6 -2
View File
@@ -32,6 +32,9 @@
| `-ub, --ubatch-size N` | physical maximum batch size (default: 512)<br/>(env: LLAMA_ARG_UBATCH) | | `-ub, --ubatch-size N` | physical maximum batch size (default: 512)<br/>(env: LLAMA_ARG_UBATCH) |
| `--keep N` | number of tokens to keep from the initial prompt (default: 0, -1 = all) | | `--keep N` | number of tokens to keep from the initial prompt (default: 0, -1 = all) |
| `--swa-full` | use full-size SWA cache (default: false)<br/>[(more info)](https://github.com/ggml-org/llama.cpp/pull/13194#issuecomment-2868343055)<br/>(env: LLAMA_ARG_SWA_FULL) | | `--swa-full` | use full-size SWA cache (default: false)<br/>[(more info)](https://github.com/ggml-org/llama.cpp/pull/13194#issuecomment-2868343055)<br/>(env: LLAMA_ARG_SWA_FULL) |
| `--token-cache-size N` | shared tokenizer and tokenized-text cache size in MiB (default: 5120, 0 = disable)<br/>(env: LLAMA_ARG_TOKEN_CACHE_SIZE) |
| `--token-cache-dir PATH` | path to the shared tokenizer and tokenized-text cache database<br/>(env: LLAMA_ARG_TOKEN_CACHE_DIR) |
| `--token-cache-clear` | clear the shared tokenizer and tokenized-text cache before loading the model<br/>(env: LLAMA_ARG_TOKEN_CACHE_CLEAR) |
| `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) | | `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) |
| `-p, --prompt PROMPT` | prompt to start generation with; for system message, use -sys | | `-p, --prompt PROMPT` | prompt to start generation with; for system message, use -sys |
| `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) | | `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) |
@@ -54,11 +57,12 @@
| `-ctv, --cache-type-v TYPE` | KV cache data type for V<br/>allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1<br/>(default: f16)<br/>(env: LLAMA_ARG_CACHE_TYPE_V) | | `-ctv, --cache-type-v TYPE` | KV cache data type for V<br/>allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1<br/>(default: f16)<br/>(env: LLAMA_ARG_CACHE_TYPE_V) |
| `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)<br/>(env: LLAMA_ARG_DEFRAG_THOLD) | | `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)<br/>(env: LLAMA_ARG_DEFRAG_THOLD) |
| `-np, --parallel N` | number of parallel sequences to decode (default: 1)<br/>(env: LLAMA_ARG_N_PARALLEL) | | `-np, --parallel N` | number of parallel sequences to decode (default: 1)<br/>(env: LLAMA_ARG_N_PARALLEL) |
| `--rpc SERVERS` | comma-separated list of RPC servers (host:port)<br/>(env: LLAMA_ARG_RPC) |
| `--mlock` | DEPRECATED in favor of `--load-mode`: force system to keep model in RAM rather than swapping or compressing<br/>(env: LLAMA_ARG_MLOCK) | | `--mlock` | DEPRECATED in favor of `--load-mode`: force system to keep model in RAM rather than swapping or compressing<br/>(env: LLAMA_ARG_MLOCK) |
| `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) | | `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) |
| `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) | | `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) |
| `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/><br/>(env: LLAMA_ARG_LOAD_MODE) | | `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/>- longhaul: stream routed MoE experts through a bounded cache<br/><br/>(env: LLAMA_ARG_LOAD_MODE) |
| `--longhaul` | stream routed Qwen3.5 MoE experts through a bounded Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul-cache N` | longhaul expert cache size in GiB<br/>(env: LLAMA_ARG_LONGHAUL_CACHE) |
| `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) | | `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) |
| `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) | | `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) |
| `--list-devices` | print list of available devices and exit | | `--list-devices` | print list of available devices and exit |
+6 -2
View File
@@ -115,6 +115,9 @@ llama-completion.exe -m models\gemma-1.1-7b-it.Q4_K_M.gguf --ignore-eos -n -1
| `-ub, --ubatch-size N` | physical maximum batch size (default: 512)<br/>(env: LLAMA_ARG_UBATCH) | | `-ub, --ubatch-size N` | physical maximum batch size (default: 512)<br/>(env: LLAMA_ARG_UBATCH) |
| `--keep N` | number of tokens to keep from the initial prompt (default: 0, -1 = all) | | `--keep N` | number of tokens to keep from the initial prompt (default: 0, -1 = all) |
| `--swa-full` | use full-size SWA cache (default: false)<br/>[(more info)](https://github.com/ggml-org/llama.cpp/pull/13194#issuecomment-2868343055)<br/>(env: LLAMA_ARG_SWA_FULL) | | `--swa-full` | use full-size SWA cache (default: false)<br/>[(more info)](https://github.com/ggml-org/llama.cpp/pull/13194#issuecomment-2868343055)<br/>(env: LLAMA_ARG_SWA_FULL) |
| `--token-cache-size N` | shared tokenizer and tokenized-text cache size in MiB (default: 5120, 0 = disable)<br/>(env: LLAMA_ARG_TOKEN_CACHE_SIZE) |
| `--token-cache-dir PATH` | path to the shared tokenizer and tokenized-text cache database<br/>(env: LLAMA_ARG_TOKEN_CACHE_DIR) |
| `--token-cache-clear` | clear the shared tokenizer and tokenized-text cache before loading the model<br/>(env: LLAMA_ARG_TOKEN_CACHE_CLEAR) |
| `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) | | `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) |
| `-p, --prompt PROMPT` | prompt to start generation with; for system message, use -sys | | `-p, --prompt PROMPT` | prompt to start generation with; for system message, use -sys |
| `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) | | `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) |
@@ -137,11 +140,12 @@ llama-completion.exe -m models\gemma-1.1-7b-it.Q4_K_M.gguf --ignore-eos -n -1
| `-ctv, --cache-type-v TYPE` | KV cache data type for V<br/>allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1<br/>(default: f16)<br/>(env: LLAMA_ARG_CACHE_TYPE_V) | | `-ctv, --cache-type-v TYPE` | KV cache data type for V<br/>allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1<br/>(default: f16)<br/>(env: LLAMA_ARG_CACHE_TYPE_V) |
| `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)<br/>(env: LLAMA_ARG_DEFRAG_THOLD) | | `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)<br/>(env: LLAMA_ARG_DEFRAG_THOLD) |
| `-np, --parallel N` | number of parallel sequences to decode (default: 1)<br/>(env: LLAMA_ARG_N_PARALLEL) | | `-np, --parallel N` | number of parallel sequences to decode (default: 1)<br/>(env: LLAMA_ARG_N_PARALLEL) |
| `--rpc SERVERS` | comma-separated list of RPC servers (host:port)<br/>(env: LLAMA_ARG_RPC) |
| `--mlock` | DEPRECATED in favor of `--load-mode`: force system to keep model in RAM rather than swapping or compressing<br/>(env: LLAMA_ARG_MLOCK) | | `--mlock` | DEPRECATED in favor of `--load-mode`: force system to keep model in RAM rather than swapping or compressing<br/>(env: LLAMA_ARG_MLOCK) |
| `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) | | `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) |
| `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) | | `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) |
| `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/><br/>(env: LLAMA_ARG_LOAD_MODE) | | `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/>- longhaul: stream routed MoE experts through a bounded cache<br/><br/>(env: LLAMA_ARG_LOAD_MODE) |
| `--longhaul` | stream routed Qwen3.5 MoE experts through a bounded Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul-cache N` | longhaul expert cache size in GiB<br/>(env: LLAMA_ARG_LONGHAUL_CACHE) |
| `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) | | `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) |
| `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) | | `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) |
| `--list-devices` | print list of available devices and exit | | `--list-devices` | print list of available devices and exit |
+8 -2
View File
@@ -53,6 +53,9 @@ For the full list of features, please refer to [server's changelog](https://gith
| `-ub, --ubatch-size N` | physical maximum batch size (default: 512)<br/>(env: LLAMA_ARG_UBATCH) | | `-ub, --ubatch-size N` | physical maximum batch size (default: 512)<br/>(env: LLAMA_ARG_UBATCH) |
| `--keep N` | number of tokens to keep from the initial prompt (default: 0, -1 = all) | | `--keep N` | number of tokens to keep from the initial prompt (default: 0, -1 = all) |
| `--swa-full` | use full-size SWA cache (default: false)<br/>[(more info)](https://github.com/ggml-org/llama.cpp/pull/13194#issuecomment-2868343055)<br/>(env: LLAMA_ARG_SWA_FULL) | | `--swa-full` | use full-size SWA cache (default: false)<br/>[(more info)](https://github.com/ggml-org/llama.cpp/pull/13194#issuecomment-2868343055)<br/>(env: LLAMA_ARG_SWA_FULL) |
| `--token-cache-size N` | shared tokenizer and tokenized-text cache size in MiB (default: 5120, 0 = disable)<br/>(env: LLAMA_ARG_TOKEN_CACHE_SIZE) |
| `--token-cache-dir PATH` | path to the shared tokenizer and tokenized-text cache database<br/>(env: LLAMA_ARG_TOKEN_CACHE_DIR) |
| `--token-cache-clear` | clear the shared tokenizer and tokenized-text cache before loading the model<br/>(env: LLAMA_ARG_TOKEN_CACHE_CLEAR) |
| `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) | | `-fa, --flash-attn [on\|off\|auto]` | set Flash Attention use ('on', 'off', or 'auto', default: 'auto')<br/>(env: LLAMA_ARG_FLASH_ATTN) |
| `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) | | `--perf, --no-perf` | whether to enable internal libllama performance timings (default: false)<br/>(env: LLAMA_ARG_PERF) |
| `-e, --escape, --no-escape` | whether to process escapes sequences (\n, \r, \t, \', \", \\) (default: true) | | `-e, --escape, --no-escape` | whether to process escapes sequences (\n, \r, \t, \', \", \\) (default: true) |
@@ -71,11 +74,12 @@ For the full list of features, please refer to [server's changelog](https://gith
| `-ctk, --cache-type-k TYPE` | KV cache data type for K<br/>allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1<br/>(default: f16)<br/>(env: LLAMA_ARG_CACHE_TYPE_K) | | `-ctk, --cache-type-k TYPE` | KV cache data type for K<br/>allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1<br/>(default: f16)<br/>(env: LLAMA_ARG_CACHE_TYPE_K) |
| `-ctv, --cache-type-v TYPE` | KV cache data type for V<br/>allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1<br/>(default: f16)<br/>(env: LLAMA_ARG_CACHE_TYPE_V) | | `-ctv, --cache-type-v TYPE` | KV cache data type for V<br/>allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1<br/>(default: f16)<br/>(env: LLAMA_ARG_CACHE_TYPE_V) |
| `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)<br/>(env: LLAMA_ARG_DEFRAG_THOLD) | | `-dt, --defrag-thold N` | KV cache defragmentation threshold (DEPRECATED)<br/>(env: LLAMA_ARG_DEFRAG_THOLD) |
| `--rpc SERVERS` | comma-separated list of RPC servers (host:port)<br/>(env: LLAMA_ARG_RPC) |
| `--mlock` | DEPRECATED in favor of `--load-mode`: force system to keep model in RAM rather than swapping or compressing<br/>(env: LLAMA_ARG_MLOCK) | | `--mlock` | DEPRECATED in favor of `--load-mode`: force system to keep model in RAM rather than swapping or compressing<br/>(env: LLAMA_ARG_MLOCK) |
| `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) | | `--mmap, --no-mmap` | DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>(env: LLAMA_ARG_MMAP) |
| `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) | | `-dio, --direct-io, -ndio, --no-direct-io` | DEPRECATED in favor of `--load-mode`: use DirectIO if available<br/>(env: LLAMA_ARG_DIO) |
| `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/><br/>(env: LLAMA_ARG_LOAD_MODE) | | `-lm, --load-mode MODE` | model loading mode (default: mmap)<br/>- none: no special loading mode<br/>- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)<br/>- mlock: force system to keep model in RAM rather than swapping or compressing<br/>- mmap+mlock: mmap + force system to keep model in RAM rather than swapping or compressing<br/>- dio: use DirectIO if available<br/>- longhaul: stream routed MoE experts through a bounded cache<br/><br/>(env: LLAMA_ARG_LOAD_MODE) |
| `--longhaul` | stream routed Qwen3.5 MoE experts through a bounded Metal cache<br/>(env: LLAMA_ARG_LONGHAUL) |
| `--longhaul-cache N` | longhaul expert cache size in GiB<br/>(env: LLAMA_ARG_LONGHAUL_CACHE) |
| `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) | | `--numa TYPE` | attempt optimizations that help on some NUMA systems<br/>- distribute: spread execution evenly over all nodes<br/>- isolate: only spawn threads on CPUs on the node that execution started on<br/>- numactl: use the CPU map provided by numactl<br/>if run without this previously, it is recommended to drop the system page cache before using this<br/>see https://github.com/ggml-org/llama.cpp/issues/1437<br/>(env: LLAMA_ARG_NUMA) |
| `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) | | `-dev, --device <dev1,dev2,..>` | comma-separated list of devices to use for offloading (none = don't offload)<br/>use --list-devices to see a list of available devices<br/>(env: LLAMA_ARG_DEVICE) |
| `--list-devices` | print list of available devices and exit | | `--list-devices` | print list of available devices and exit |
@@ -199,6 +203,8 @@ For the full list of features, please refer to [server's changelog](https://gith
| `--ui-config-file, --webui-config-file PATH` | JSON file that provides default UI settings (overrides UI defaults)<br/>(env: LLAMA_ARG_UI_CONFIG_FILE) | | `--ui-config-file, --webui-config-file PATH` | JSON file that provides default UI settings (overrides UI defaults)<br/>(env: LLAMA_ARG_UI_CONFIG_FILE) |
| `--ui-mcp-proxy, --webui-mcp-proxy, --no-ui-mcp-proxy, --no-webui-mcp-proxy` | experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: disabled)<br/>(env: LLAMA_ARG_UI_MCP_PROXY) | | `--ui-mcp-proxy, --webui-mcp-proxy, --no-ui-mcp-proxy, --no-webui-mcp-proxy` | experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: disabled)<br/>(env: LLAMA_ARG_UI_MCP_PROXY) |
| `--tools TOOL1,TOOL2,...` | experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)<br/>specify "all" to enable all tools<br/>available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, get_datetime<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_TOOLS) | | `--tools TOOL1,TOOL2,...` | experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)<br/>specify "all" to enable all tools<br/>available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, get_datetime<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_TOOLS) |
| `--mcp-servers-config PATH` | experimental: path to JSON file with MCP server definitions (Cursor-compatible format) - do not enable in untrusted environments (default: none)<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_MCP_SERVERS_CONFIG) |
| `--mcp-servers-json JSON` | experimental: inline JSON with MCP server definitions (Cursor-compatible format) - do not enable in untrusted environments (default: none)<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_MCP_SERVERS_JSON) |
| `-ag, --agent, -no-ag, --no-agent` | whether to enable CORS proxy and all built-in tools - do not enable in untrusted environments (default: disabled)<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_AGENT) | | `-ag, --agent, -no-ag, --no-agent` | whether to enable CORS proxy and all built-in tools - do not enable in untrusted environments (default: disabled)<br/>note: for security reasons, this will limit --cors-origins to localhost by default<br/>(env: LLAMA_ARG_AGENT) |
| `--ui, --webui, --no-ui, --no-webui` | whether to enable the Web UI (default: enabled)<br/>(env: LLAMA_ARG_UI) | | `--ui, --webui, --no-ui, --no-webui` | whether to enable the Web UI (default: enabled)<br/>(env: LLAMA_ARG_UI) |
| `--embedding, --embeddings` | restrict to only support embedding use case; use only with dedicated embedding models (default: disabled)<br/>(env: LLAMA_ARG_EMBEDDINGS) | | `--embedding, --embeddings` | restrict to only support embedding use case; use only with dedicated embedding models (default: disabled)<br/>(env: LLAMA_ARG_EMBEDDINGS) |
+11 -4
View File
@@ -38,6 +38,7 @@
using json = nlohmann::ordered_json; using json = nlohmann::ordered_json;
constexpr int HTTP_POLLING_SECONDS = 1; constexpr int HTTP_POLLING_SECONDS = 1;
constexpr int64_t PROMPT_TIMING_LOG_INTERVAL_US = 5*1000*1000;
static uint32_t server_n_outputs_max(const common_params & params) { static uint32_t server_n_outputs_max(const common_params & params) {
const uint32_t n_batch = params.n_batch; const uint32_t n_batch = params.n_batch;
@@ -276,6 +277,7 @@ struct server_slot {
// TODO @ngxson : move all metrics to a sub-struct for clarity // TODO @ngxson : move all metrics to a sub-struct for clarity
int64_t t_start_process_prompt; int64_t t_start_process_prompt;
int64_t t_start_generation; int64_t t_start_generation;
int64_t t_prompt_print_last = 0;
int64_t t_print_last = 0; int64_t t_print_last = 0;
int32_t n_decoded_last = 0; int32_t n_decoded_last = 0;
@@ -569,13 +571,17 @@ struct server_slot {
SLT_INF(*this, "n_decoded = %6d, tg = %6.2f t/s, tg_3s = %6.2f t/s\n", n_decoded, n_gen_second, n_gen_second_win); SLT_INF(*this, "n_decoded = %6d, tg = %6.2f t/s, tg_3s = %6.2f t/s\n", n_decoded, n_gen_second, n_gen_second_win);
} }
void print_timings_pp() const { void print_timings_pp() {
const int64_t t_now = ggml_time_us();
if (t_now - t_prompt_print_last < PROMPT_TIMING_LOG_INTERVAL_US) {
return;
}
const double n_prompt_second = 1e3 / t_prompt_processing * n_prompt_tokens_processed; const double n_prompt_second = 1e3 / t_prompt_processing * n_prompt_tokens_processed;
const double f_progress = (float) prompt.n_tokens() / task->n_tokens(); const double f_progress = (float) prompt.n_tokens() / task->n_tokens();
if (t_prompt_processing < 3000.0) { t_prompt_print_last = t_now;
return;
}
SLT_INF(*this, "prompt processing, n_tokens = %6d, progress = %.2f, t = %6.2f s / %.2f tokens per second\n", SLT_INF(*this, "prompt processing, n_tokens = %6d, progress = %.2f, t = %6.2f s / %.2f tokens per second\n",
n_prompt_tokens_processed, f_progress, t_prompt_processing / 1e3, n_prompt_second); n_prompt_tokens_processed, f_progress, t_prompt_processing / 1e3, n_prompt_second);
@@ -3072,6 +3078,7 @@ private:
// TODO: maybe move branch to outside of this loop in the future // TODO: maybe move branch to outside of this loop in the future
if (slot.state == SLOT_STATE_STARTED) { if (slot.state == SLOT_STATE_STARTED) {
slot.t_start_process_prompt = ggml_time_us(); slot.t_start_process_prompt = ggml_time_us();
slot.t_prompt_print_last = slot.t_start_process_prompt;
slot.t_start_generation = 0; slot.t_start_generation = 0;
slot.state = SLOT_STATE_PROCESSING_PROMPT; slot.state = SLOT_STATE_PROCESSING_PROMPT;