Skip to content

Commit a526aa5

Browse files
authored
Merge pull request #8 from kmbandy/feature-kv-cache-improvements
Tiered kv cache implementation along with vattention (mad-lab-cache)
2 parents b25eddd + 786f501 commit a526aa5

38 files changed

Lines changed: 5901 additions & 18 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ poetry.toml
144144
/*.code-workspace
145145
/.windsurf/
146146
# emscripten
147-
a.out.*
147+
a.out.*x
148148
.gitnexus
149149
.claude/
150150
.agent.md
@@ -175,4 +175,5 @@ ggml/ggml-version.cmake
175175
tests/libgguf-model-data.a
176176
*.a
177177
tests/cmake_install.cmake
178+
Makefile
178179
*.cmake

common/arg.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <list>
3232
#include <regex>
3333
#include <set>
34+
#include <sstream>
3435
#include <string>
3536
#include <thread> // for hardware_concurrency
3637
#include <vector>
@@ -1310,6 +1311,81 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
13101311
params.cache_ram_mib = value;
13111312
}
13121313
).set_env("LLAMA_ARG_CACHE_RAM").set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}));
1314+
add_opt(common_arg(
1315+
{"--kv-tiered"}, "VRAM%,RAM%,SSD%",
1316+
"enable tiered KV cache with percentages for VRAM, RAM, SSD (e.g., 25,25,50 for 25% VRAM, 25% RAM, 50% SSD)",
1317+
[](common_params & params, const std::string & value) {
1318+
params.kv_tiered_enabled = true;
1319+
// Parse the percentage string
1320+
std::vector<std::string> parts;
1321+
std::stringstream ss(value);
1322+
std::string part;
1323+
while (std::getline(ss, part, ',')) {
1324+
parts.push_back(part);
1325+
}
1326+
if (parts.size() == 3) {
1327+
params.kv_tier_hot_pct = std::stof(parts[0]);
1328+
params.kv_tier_warm_pct = std::stof(parts[1]);
1329+
params.kv_tier_cold_pct = std::stof(parts[2]);
1330+
}
1331+
}
1332+
).set_env("LLAMA_ARG_KT_TIERED").set_examples({LLAMA_EXAMPLE_SERVER}));
1333+
add_opt(common_arg(
1334+
{"--tier-ssd-path"}, "PATH",
1335+
"directory for cold tier (SSD) storage",
1336+
[](common_params & params, const std::string & value) {
1337+
params.kv_tier_ssd_path = value;
1338+
}
1339+
).set_env("LLAMA_ARG_TIER_SSD_PATH").set_examples({LLAMA_EXAMPLE_SERVER}));
1340+
add_opt(common_arg(
1341+
{"--tier-eviction-policy"}, "POLICY",
1342+
"eviction policy: 0=LRU, 1=LFU, 2=attention, 3=hybrid (default)",
1343+
[](common_params & params, int value) {
1344+
params.kv_tier_eviction_policy = value;
1345+
}
1346+
).set_env("LLAMA_ARG_TIER_EVICTION_POLICY").set_examples({LLAMA_EXAMPLE_SERVER}));
1347+
add_opt(common_arg(
1348+
{"--tier-compression"}, "TYPE",
1349+
"compression type: 0=none, 1=int4, 2=int8, 3=lz4, 4=quantized",
1350+
[](common_params & params, int value) {
1351+
params.kv_tier_compression = value;
1352+
}
1353+
).set_env("LLAMA_ARG_TIER_COMPRESSION").set_examples({LLAMA_EXAMPLE_SERVER}));
1354+
add_opt(common_arg(
1355+
{"--tier-attention-threshold"}, "THRESH",
1356+
"attention threshold for eviction (0.0-1.0, default: 0.1)",
1357+
[](common_params & params, int value) {
1358+
params.kv_tier_attention_threshold = float(value) / 100.0f;
1359+
}
1360+
).set_env("LLAMA_ARG_TIER_ATTENTION_THRESHOLD").set_examples({LLAMA_EXAMPLE_SERVER}));
1361+
add_opt(common_arg(
1362+
{"--kv-warm-device"}, "DEVICE",
1363+
"HIP device index to use as warm KV cache tier (e.g. 1 for 6900XT eGPU); requires --kv-tiered",
1364+
[](common_params & params, int value) {
1365+
params.kv_warm_device = value;
1366+
}
1367+
).set_env("LLAMA_ARG_KV_WARM_DEVICE").set_examples({LLAMA_EXAMPLE_SERVER}));
1368+
add_opt(common_arg(
1369+
{"--kv-semantic-index"}, "PATH",
1370+
"path to embedding model (GGUF) for semantic KV cache indexing (e.g. bge-small); empty = disabled",
1371+
[](common_params & params, const std::string & value) {
1372+
params.kv_semantic_index = value;
1373+
}
1374+
).set_env("LLAMA_ARG_KV_SEMANTIC_INDEX").set_examples({LLAMA_EXAMPLE_SERVER}));
1375+
add_opt(common_arg(
1376+
{"--kv-semantic-threshold"}, "THRESHOLD",
1377+
"minimum cosine similarity threshold for semantic prefetch hints (default: 0.65)",
1378+
[](common_params & params, const std::string & value) {
1379+
params.kv_semantic_threshold = std::stof(value);
1380+
}
1381+
).set_env("LLAMA_ARG_KV_SEMANTIC_THRESHOLD").set_examples({LLAMA_EXAMPLE_SERVER}));
1382+
add_opt(common_arg(
1383+
{"--kv-semantic-topk"}, "K",
1384+
"number of prefetch hints to return (default: 5)",
1385+
[](common_params & params, int value) {
1386+
params.kv_semantic_top_k = value;
1387+
}
1388+
).set_env("LLAMA_ARG_KV_SEMANTIC_TOPK").set_examples({LLAMA_EXAMPLE_SERVER}));
13131389
add_opt(common_arg(
13141390
{"-kvu", "--kv-unified"},
13151391
{"-no-kvu", "--no-kv-unified"},
@@ -2354,6 +2430,27 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
23542430
}
23552431
}
23562432
).set_env("LLAMA_ARG_N_GPU_LAYERS"));
2433+
add_opt(common_arg(
2434+
{"--weight-paging"},
2435+
"enable NVMe→VRAM demand paging for model weights (allows models larger than VRAM)",
2436+
[](common_params & params) {
2437+
params.weight_paging_enabled = true;
2438+
}
2439+
).set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_WEIGHT_PAGING"));
2440+
add_opt(common_arg(
2441+
{"--weight-paging-slots"}, "N",
2442+
"number of VRAM slots for weight paging (-1 = auto, default: -1)",
2443+
[](common_params & params, const std::string & value) {
2444+
params.weight_paging_slots = std::stoi(value);
2445+
}
2446+
).set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_WEIGHT_PAGING_SLOTS"));
2447+
add_opt(common_arg(
2448+
{"--weight-paging-prefetch"},
2449+
"enable async prefetch of next layer (default: enabled)",
2450+
[](common_params & params) {
2451+
params.weight_paging_prefetch = true;
2452+
}
2453+
).set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_WEIGHT_PAGING_PREFETCH"));
23572454
add_opt(common_arg(
23582455
{"-sm", "--split-mode"}, "{none,layer,row,tensor}",
23592456
"how to split the model across multiple GPUs, one of:\n"

common/common.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1343,7 +1343,7 @@ common_init_result_ptr common_init_from_params(common_params & params) {
13431343
common_set_adapter_lora(lctx, params.lora_adapters);
13441344
}
13451345

1346-
if (params.warmup) {
1346+
if (params.warmup && !params.weight_paging_enabled) {
13471347
LOG_WRN("%s: warming up the model with an empty run - please wait ... (--no-warmup to disable)\n", __func__);
13481348

13491349
llama_set_warmup(lctx, true);
@@ -1488,6 +1488,11 @@ struct llama_model_params common_model_params_to_llama(common_params & params) {
14881488
mparams.progress_callback_user_data = params.load_progress_callback_user_data;
14891489
mparams.no_alloc = params.no_alloc;
14901490

1491+
// Weight paging parameters
1492+
mparams.weight_paging_enabled = params.weight_paging_enabled;
1493+
mparams.weight_paging_slots = params.weight_paging_slots;
1494+
mparams.weight_paging_prefetch = params.weight_paging_prefetch;
1495+
14911496
return mparams;
14921497
}
14931498

common/common.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,11 @@ struct common_params {
533533
bool no_extra_bufts = false; // disable extra buffer types (used for weight repacking)
534534
bool no_host = false; // bypass host buffer allowing extra buffers to be used
535535

536+
// weight paging parameters
537+
bool weight_paging_enabled = false; // enable NVMe→VRAM demand paging for model weights
538+
int32_t weight_paging_slots = -1; // number of VRAM slots for weight paging (-1 = auto)
539+
bool weight_paging_prefetch = false; // enable async prefetch of next layer
540+
536541
bool single_turn = false; // single turn chat conversation
537542

538543
ggml_type cache_type_k = GGML_TYPE_F16; // KV cache data type for the K
@@ -573,6 +578,21 @@ struct common_params {
573578
int32_t checkpoint_every_nt = 8192; // make a checkpoint every n tokens during prefill
574579
int32_t cache_ram_mib = 8192; // -1 = no limit, 0 - disable, 1 = 1 MiB, etc.
575580

581+
// tiered KV cache parameters
582+
bool kv_tiered_enabled = false; // enable tiered KV cache
583+
float kv_tier_hot_pct = 25.0f; // hot tier percentage (VRAM)
584+
float kv_tier_warm_pct = 25.0f; // warm tier percentage (RAM)
585+
float kv_tier_cold_pct = 50.0f; // cold tier percentage (SSD)
586+
std::string kv_tier_ssd_path = ""; // SSD path for cold tier storage
587+
int kv_tier_eviction_policy = 3; // 0=LRU, 1=LFU, 2=attention, 3=hybrid (default)
588+
int kv_tier_compression = 1; // 0=none, 1=int4, 2=int8, 3=lz4, 4=quantized
589+
float kv_tier_attention_threshold = 0.1f; // attention threshold for eviction
590+
int kv_warm_device = -1; // HIP device index for warm KV tier (-1 = disabled)
591+
int kv_tier_total_ctx = 0; // full ctx budget across all tiers (set at load time)
592+
std::string kv_semantic_index = ""; // path to embedding model for semantic KV index (empty = disabled)
593+
float kv_semantic_threshold = 0.65f; // minimum cosine similarity threshold for prefetch hints
594+
int kv_semantic_top_k = 5; // number of prefetch hints to return
595+
576596
std::string hostname = "127.0.0.1";
577597
std::string public_path = ""; // NOLINT
578598
std::string api_prefix = ""; // NOLINT

common/fit.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data(
5353
}, &ud);
5454

5555
llama_model_params mparams_copy = *mparams;
56-
mparams_copy.no_alloc = true;
57-
mparams_copy.use_mmap = false;
58-
mparams_copy.use_mlock = false;
56+
mparams_copy.no_alloc = true;
57+
mparams_copy.use_mmap = false;
58+
mparams_copy.use_mlock = false;
59+
mparams_copy.use_direct_io = false;
5960

6061
llama_model * model = llama_model_load_from_file(path_model, mparams_copy);
6162
if (model == nullptr) {

ggml/src/ggml-cpu/ops.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#include <cfloat>
1313
#include <cmath>
1414

15-
extern "C" GGML_API int turbo3_cpu_wht_group_size;
15+
extern "C" int turbo3_cpu_wht_group_size;
1616

1717
// ggml_compute_forward_dup
1818

ggml/src/ggml-cuda/common.cuh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ struct ggml_cuda_graph {
11931193
std::vector<node_properties> node_props;
11941194

11951195
bool is_enabled() const {
1196-
static const bool disable_cuda_graphs_due_to_env = (getenv("GGML_CUDA_DISABLE_GRAPHS") != nullptr);
1196+
const bool disable_cuda_graphs_due_to_env = (getenv("GGML_CUDA_DISABLE_GRAPHS") != nullptr);
11971197
return !(disable_due_to_gpu_arch || disable_cuda_graphs_due_to_env);
11981198
}
11991199
#endif

ggml/src/ggml-cuda/ggml-cuda.cu

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,54 @@ static cudaError_t ggml_cuda_cpy_tensor_2d(
14191419
const int64_t i1_diff = i1_high - i1_low;
14201420

14211421
const char * x = src_ptr + i1_low*nb1 + i2*nb2 + i3*nb3;
1422+
1423+
#if defined(GGML_USE_HIP)
1424+
// hipMemcpy2DAsync with hipMemcpyDeviceToDevice requires P2P access between devices.
1425+
// For mixed-architecture multi-GPU setups (e.g. gfx1201 + gfx1030) without P2P,
1426+
// it fails asynchronously with hipErrorNoBinaryForGpu when the internal copy kernel
1427+
// isn't compiled for the destination device. Detect cross-device copies via pointer
1428+
// attributes and fall back to row-wise hipMemcpyPeerAsync, which stages through the
1429+
// host when P2P is unavailable. Mirrors the fix in ggml_cuda_Memcpy2DPeerAsync.
1430+
hipPointerAttribute_t src_attr = {};
1431+
hipPointerAttribute_t dst_attr = {};
1432+
const bool src_ok = hipPointerGetAttributes(&src_attr, x) == hipSuccess;
1433+
const bool dst_ok = hipPointerGetAttributes(&dst_attr, dst_ptr) == hipSuccess;
1434+
if (src_ok && dst_ok && src_attr.device != dst_attr.device) {
1435+
const int src_dev = src_attr.device;
1436+
const int dst_dev = dst_attr.device;
1437+
if (nb0 == ts && nb1 == ts*ne0/bs) {
1438+
return hipMemcpyPeerAsync(dst_ptr, dst_dev, x, src_dev, i1_diff*nb1, stream);
1439+
} else if (nb0 == ts) {
1440+
const size_t row_bytes = ts*ne0/bs;
1441+
for (int64_t i1 = 0; i1 < i1_diff; i1++) {
1442+
hipError_t err = hipMemcpyPeerAsync(
1443+
dst_ptr + i1*row_bytes, dst_dev,
1444+
x + i1*nb1, src_dev,
1445+
row_bytes, stream);
1446+
if (err != hipSuccess) {
1447+
return err;
1448+
}
1449+
}
1450+
return hipSuccess;
1451+
} else {
1452+
for (int64_t i1 = 0; i1 < i1_diff; i1++) {
1453+
const char * rx = x + i1*nb1;
1454+
char * rd = dst_ptr + i1*ts*ne0/bs;
1455+
for (int64_t e = 0; e < ne0; e++) {
1456+
hipError_t err = hipMemcpyPeerAsync(
1457+
rd + e*(ts/bs), dst_dev,
1458+
rx + e*nb0, src_dev,
1459+
ts/bs, stream);
1460+
if (err != hipSuccess) {
1461+
return err;
1462+
}
1463+
}
1464+
}
1465+
return hipSuccess;
1466+
}
1467+
}
1468+
#endif
1469+
14221470
if (nb0 == ts && nb1 == ts*ne0/bs) {
14231471
return cudaMemcpyAsync(dst_ptr, x, i1_diff*nb1, cudaMemcpyDeviceToDevice, stream);
14241472
} else if (nb0 == ts) {

include/llama.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,11 @@ extern "C" {
321321
bool use_extra_bufts; // use extra buffer types (used for weight repacking)
322322
bool no_host; // bypass host buffer allowing extra buffers to be used
323323
bool no_alloc; // only load metadata and simulate memory allocations
324+
325+
// Weight paging parameters (NVMe→VRAM demand paging)
326+
bool weight_paging_enabled; // enable NVMe→VRAM demand paging for model weights
327+
int32_t weight_paging_slots; // number of VRAM slots for weight paging (-1 = auto)
328+
bool weight_paging_prefetch; // enable async prefetch of next layer
324329
};
325330

326331
struct llama_sampler_seq_config {

plans/errors/command.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Here is what I use to start the server:
2+
3+
llama-server-feature \
4+
--model ~/models/MiniMax-M2.7-UD-IQ3_S-00001-of-00003.gguf \
5+
-ngl 999 \
6+
--device ROCm0 \
7+
--ctx-size 196608 \
8+
--cache-type-k turbo4 \
9+
--cache-type-v turbo4 \
10+
--flash-attn on \
11+
--metrics \
12+
--port 8080 \
13+
--host 0.0.0.0 \
14+
--no-mmap \
15+
--kv-tiered 12.5,50,37.5 \
16+
--tier-ssd-path ~/kv-cold \
17+
--tier-eviction-policy 3 \
18+
--parallel 1 \
19+
--kv-semantic-index ~/models/bge-small-en-v1.5-q8_0.gguf \
20+
--kv-semantic-threshold 0.65 \
21+
--kv-semantic-topk 5 \
22+
--alias default \
23+
--direct-io \
24+
--kv-warm-device 1 \
25+
--weight-paging \
26+
--fit off \
27+
--weight-paging-slots 8 \
28+
--jinja

0 commit comments

Comments
 (0)