Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
fd9eb46
Add DeepSeek V4 GGUF conversion
nisparks Apr 25, 2026
8e6ee61
Optimize GGUF conversion paths
nisparks Apr 25, 2026
a922f7d
Bring up native FP4 FP8 quant support
nisparks Apr 25, 2026
5ec1ff6
WIP DeepSeek V4 runtime support
nisparks Apr 26, 2026
bc341ef
Implement DeepSeek4 runtime state save
nisparks Apr 26, 2026
43de75b
Tune DeepSeek4 F8 scale decode
nisparks Apr 26, 2026
f69bf66
Port DeepSeek4 performance hot paths
nisparks Apr 26, 2026
781246c
Tune fused DeepSeek4 F8 MMVQ
nisparks Apr 26, 2026
14660a6
Add CUDA warp TOP_K fast path
nisparks Apr 26, 2026
c2744c7
Tune DeepSeek4 F8 row blocking
nisparks Apr 26, 2026
a299c77
Tune Q8 activation quantization
nisparks Apr 26, 2026
55acc5b
Tune DeepSeek4 copy and RMSNorm kernels
nisparks Apr 26, 2026
056d7a5
Avoid DeepSeek4 hc_post vector transpose
nisparks Apr 26, 2026
f29cbee
Add DeepSeek4 HC weighted sum op
nisparks Apr 26, 2026
ee9e652
Improve prompt cache reuse for full-removal memory
nisparks Apr 26, 2026
b990219
Test prompt cache full-removal allocation
nisparks Apr 26, 2026
cdbc7ba
Broaden HC weighted-sum test shapes
nisparks Apr 26, 2026
a4ef65f
Avoid FP8 packer scale expansion temporary
nisparks Apr 26, 2026
0bc9344
Avoid MXFP4 packer nibble expansion
nisparks Apr 26, 2026
ec69799
Validate DeepSeek4 native scale storage
nisparks Apr 26, 2026
a7d9255
Harden HC weighted-sum shape checks
nisparks Apr 26, 2026
0afe2c9
Cover F8 in CPU unsupported op switches
nisparks Apr 26, 2026
8c8641f
Complete F8 CPU op switch coverage
nisparks Apr 26, 2026
ba5dcb0
Test DeepSeek4 native packers
nisparks Apr 26, 2026
86a851b
Keep DeepSeek4 packer test Python 3.8 compatible
nisparks Apr 26, 2026
32bec0e
Add MoE selective-copy trace logging
nisparks Apr 27, 2026
12ae263
Add MoE copy LRU simulator
nisparks Apr 27, 2026
a635524
Keep MoE LRU simulator within slot budget
nisparks Apr 27, 2026
85a5596
Report MoE LRU cache footprint
nisparks Apr 27, 2026
6f45300
Validate MoE LRU trace metadata
nisparks Apr 27, 2026
868cd1f
Guard MoE LRU simulator byte accounting
nisparks Apr 27, 2026
933ea33
Prototype MoE LRU expert cache
nisparks Apr 27, 2026
f069485
Harden MoE cache slot parsing
nisparks Apr 27, 2026
6309b76
Clear MoE cache tensor metadata
nisparks Apr 27, 2026
3944c54
Summarize MoE runtime cache logs
nisparks Apr 27, 2026
6f2ca57
Document MoE LRU simulator modes
nisparks Apr 27, 2026
bf84c10
Key MoE ID cache by expert count
nisparks Apr 27, 2026
a3f6b9e
Guard empty MoE selective copies
nisparks Apr 27, 2026
ce3917d
Log MoE cache bypass reasons
nisparks Apr 27, 2026
8ff0511
Summarize MoE cache bypass logs
nisparks Apr 27, 2026
75962ee
Cover MoE bypass-only runtime logs
nisparks Apr 27, 2026
f38a6f2
Report MoE bypass slots
nisparks Apr 27, 2026
f79849d
Group MoE runtime stats by slots
nisparks Apr 27, 2026
506016f
Report MoE runtime cache footprint
nisparks Apr 27, 2026
65986f3
Validate MoE runtime cache footprint
nisparks Apr 27, 2026
a60eb34
Experiment with MoE LRU cache balancing
nisparks Apr 27, 2026
6c85a6d
Simulate speculative MoE expert prefetch
nisparks Apr 27, 2026
ccc7cb7
Prototype MoE set-Markov cache retention
nisparks Apr 27, 2026
dda2c9b
Prime MoE cache from prompt bypasses
nisparks Apr 27, 2026
f696ef5
Tune IQ4_XS MMVQ row blocking
nisparks Apr 27, 2026
1245d8d
Skip IQ4_XS Q8 activation sums
nisparks Apr 27, 2026
7209909
Fix DeepSeek4 arch smoke coverage
nisparks Apr 27, 2026
19e7b86
Make backend ops smoke bounded
nisparks Apr 27, 2026
95dba05
Optimize DeepSeek V4 native cache and reasoning
nisparks Apr 28, 2026
e867312
Shrink DeepSeek4 prompt-cache checkpoints to active prefix
nisparks Apr 30, 2026
9b62573
Hint MADV_HUGEPAGE for large CPU allocations on Linux
nisparks Apr 30, 2026
5ed9b4a
Add ds4-expert-profile tool to measure MoE routing skew
nisparks May 1, 2026
50f28d0
ds4-expert-profile: emit JSON hot-expert profile for runtime consumption
nisparks May 1, 2026
415ce7d
Add Phase 1 hot-expert pinning: load-time tensor extraction
nisparks May 1, 2026
8cc6dcf
Phase 2 hot-expert dispatch (work in progress)
nisparks May 1, 2026
d4bd158
Phase 2 cleanup: opt-in dispatch and single-GPU debug option
nisparks May 1, 2026
be606b8
Bump hot-pinning per-GPU margin to 1.5 GiB to avoid OOM during prefill
nisparks May 1, 2026
dfdfd2f
Phase 2: fix dispatch crash with per-pick unique dummy expert IDs
nisparks May 1, 2026
f8837fa
Phase 2: use shared cold sentinel for CPU dedup bandwidth saving
nisparks May 1, 2026
f8a7572
fix(server): prevent GGML_ABORT when prompt cache pos_min == -1
May 2, 2026
ddedfdc
ggml-cpu/x86: use VNNI in MXFP4 dot product hot path
nisparks May 4, 2026
e224696
ggml: drop dangling W4A16_AUTOROUND type entries
nisparks May 4, 2026
b2091a0
llama-memory-deepseek4: stub the missing batched-prefill split
nisparks May 4, 2026
9a28dc4
convert: register F8_E8M0 in numpy dtype map and add V4 vocab fallback
nisparks May 4, 2026
c77f143
deepseek4: unblock FA by always emitting kq_mask and padding KV to 256
nisparks May 5, 2026
1902621
ggml-cpu: support batched (n_batch > 1) inputs for HC/sinkhorn V4 ops
nisparks May 5, 2026
94ec5be
deepseek4: implement batched prefill (LLAMA_DEEPSEEK4_BATCH_PREFILL)
nisparks May 5, 2026
be3a464
deepseek4: enable batched prefill by default
nisparks May 5, 2026
2d16232
deepseek4: collapse work_tokens before indexer score; bump graph node…
nisparks May 5, 2026
0c4cc85
ggml-cuda: auto-disable CUDA graphs for wide-prefill graphs
nisparks May 5, 2026
b09db7d
ggml-cuda: support batched (n_batch > 1) HC_WEIGHTED_SUM and SINKHORN…
nisparks May 5, 2026
712a9a7
deepseek4: batch the multiwindow compression update over windows
nisparks May 5, 2026
6a953c9
ggml-cuda: refine wide-prefill CUDA-graph auto-disable check
nisparks May 6, 2026
9bc4d1d
deepseek4: default to per-query indexer top-k in batched prefill
nisparks May 6, 2026
70689ee
server: auto-disable prompt cache when model lacks partial seq removal
nisparks May 6, 2026
3a244f6
Merge pull request #1 from tokencube/fix/prompt-cache-pos-min-abort
nisparks May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
458 changes: 454 additions & 4 deletions convert_hf_to_gguf.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ else()
add_subdirectory(debug)
add_subdirectory(embedding)
add_subdirectory(eval-callback)
add_subdirectory(ds4-expert-profile)

add_subdirectory(gguf-hash)
add_subdirectory(gguf)
Expand Down
5 changes: 5 additions & 0 deletions examples/ds4-expert-profile/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
set(TARGET llama-ds4-expert-profile)
add_executable(${TARGET} ds4-expert-profile.cpp)
install(TARGETS ${TARGET} RUNTIME)
target_link_libraries(${TARGET} PRIVATE llama-common llama ${CMAKE_THREAD_LIBS_INIT})
target_compile_features(${TARGET} PRIVATE cxx_std_17)
245 changes: 245 additions & 0 deletions examples/ds4-expert-profile/ds4-expert-profile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
// Profile DeepSeek4 expert routing frequencies during inference.
//
// Captures the `ffn_topk` tensor output for each layer, builds per-layer
// expert-id histograms, and emits a JSON-ish report at the end. Use this to
// see whether routing is skewed enough to make hot-expert pinning worthwhile.

#include "arg.h"
#include "common.h"
#include "log.h"
#include "llama.h"

#include <algorithm>
#include <cinttypes>
#include <clocale>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <regex>
#include <string>
#include <vector>

struct expert_profile {
int n_layer = 0;
int n_expert = 0;
std::map<int, std::vector<uint64_t>> per_layer;
uint64_t total_token_picks = 0;
std::vector<uint8_t> scratch;
std::regex topk_re{"^ffn_topk(?:-([0-9]+))?$"};
};

static bool ds4_cb_eval(struct ggml_tensor * t, bool ask, void * user_data) {
auto * prof = (expert_profile *) user_data;
if (!t || !t->name) {
return ask ? false : true;
}

std::cmatch m;
if (!std::regex_match(t->name, m, prof->topk_re)) {
return ask ? false : true;
}

if (ask) {
return true;
}

int il = -1;
if (m.size() >= 2 && m[1].matched) {
il = std::atoi(m[1].str().c_str());
}

if (t->type != GGML_TYPE_I32) {
return true;
}

auto nbytes = ggml_nbytes(t);
prof->scratch.resize(nbytes);
if (ggml_backend_buffer_is_host(t->buffer)) {
std::memcpy(prof->scratch.data(), t->data, nbytes);
} else {
ggml_backend_tensor_get(t, prof->scratch.data(), 0, nbytes);
}

auto & hist = prof->per_layer[il];
if ((int) hist.size() < prof->n_expert) {
hist.assign(prof->n_expert, 0);
}

const int32_t * ids = (const int32_t *) prof->scratch.data();
const size_t n_elems = nbytes / sizeof(int32_t);
for (size_t i = 0; i < n_elems; ++i) {
const int32_t e = ids[i];
if (e >= 0 && e < prof->n_expert) {
hist[e]++;
prof->total_token_picks++;
}
}

return true;
}

int main(int argc, char ** argv) {
std::setlocale(LC_NUMERIC, "C");

common_params params;
common_init();

if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}

llama_backend_init();
llama_numa_init(params.numa);

expert_profile prof;
params.cb_eval = ds4_cb_eval;
params.cb_eval_user_data = &prof;
params.warmup = false;

auto llama_init = common_init_from_params(params);
auto * model = llama_init->model();
auto * ctx = llama_init->context();
if (!model || !ctx) {
LOG_ERR("failed to init\n");
return 1;
}

prof.n_layer = llama_model_n_layer(model);
prof.n_expert = 256; // hardcoded for DS4-Flash; could read from model metadata

LOG_INF("\nds4-expert-profile: model has %d layers, %d experts\n", prof.n_layer, prof.n_expert);
LOG_INF("ds4-expert-profile: prompt length: %zu chars\n", params.prompt.size());

const llama_vocab * vocab = llama_model_get_vocab(model);
const bool add_bos = llama_vocab_get_add_bos(vocab);
auto tokens = common_tokenize(ctx, params.prompt, add_bos, true);
if (tokens.empty()) {
LOG_ERR("no tokens; provide a prompt with -p\n");
return 1;
}
LOG_INF("ds4-expert-profile: tokenized to %zu tokens\n", tokens.size());

if (llama_decode(ctx, llama_batch_get_one(tokens.data(), tokens.size()))) {
LOG_ERR("decode failed\n");
return 1;
}

LOG_INF("\n=== expert routing report ===\n");
LOG_INF("total expert picks observed: %" PRIu64 "\n", prof.total_token_picks);

std::vector<int> top_ks = {8, 16, 32, 64, 128};
std::map<int, double> hot_coverage_max;
std::map<int, double> hot_coverage_avg_sum;
std::map<int, int> hot_coverage_avg_count;

LOG_INF("\nper-layer routing summary:\n");
for (auto & [il, hist] : prof.per_layer) {
if (hist.empty()) continue;

uint64_t total = 0;
for (uint64_t v : hist) total += v;
if (total == 0) continue;

std::vector<std::pair<int, uint64_t>> sorted;
sorted.reserve(hist.size());
for (size_t e = 0; e < hist.size(); ++e) {
if (hist[e] > 0) sorted.emplace_back((int) e, hist[e]);
}
std::sort(sorted.begin(), sorted.end(), [](auto & a, auto & b) {
return a.second > b.second;
});

const uint64_t hottest = sorted.empty() ? 0 : sorted.front().second;
const int unique_used = (int) sorted.size();

LOG_INF("layer %2d: total=%" PRIu64 " unique=%d hottest=%" PRIu64 "(%.1f%%)\n",
il, total, unique_used, hottest, 100.0 * hottest / total);

for (int k : top_ks) {
uint64_t sum = 0;
for (int i = 0; i < k && i < (int) sorted.size(); ++i) {
sum += sorted[i].second;
}
const double frac = 100.0 * sum / total;
hot_coverage_max[k] = std::max(hot_coverage_max[k], frac);
hot_coverage_avg_sum[k] += frac;
hot_coverage_avg_count[k] += 1;
}
}

LOG_INF("\n=== summary across layers ===\n");
LOG_INF("top-K hot expert coverage:\n");
for (int k : top_ks) {
if (hot_coverage_avg_count[k] == 0) continue;
const double avg = hot_coverage_avg_sum[k] / hot_coverage_avg_count[k];
LOG_INF(" top-%-3d avg=%.1f%% max-layer=%.1f%%\n",
k, avg, hot_coverage_max[k]);
}

LOG_INF("\nper-layer Pareto analysis (how many experts cover X%% of routings):\n");
for (auto & [il, hist] : prof.per_layer) {
if (hist.empty()) continue;
std::vector<uint64_t> sorted_h(hist);
std::sort(sorted_h.begin(), sorted_h.end(), std::greater<uint64_t>());
uint64_t total = 0;
for (uint64_t v : sorted_h) total += v;
if (total == 0) continue;

uint64_t cum = 0;
int e50 = -1, e80 = -1, e90 = -1, e95 = -1, e99 = -1;
for (size_t i = 0; i < sorted_h.size(); ++i) {
cum += sorted_h[i];
if (e50 < 0 && cum * 100 >= total * 50) e50 = (int)(i + 1);
if (e80 < 0 && cum * 100 >= total * 80) e80 = (int)(i + 1);
if (e90 < 0 && cum * 100 >= total * 90) e90 = (int)(i + 1);
if (e95 < 0 && cum * 100 >= total * 95) e95 = (int)(i + 1);
if (e99 < 0 && cum * 100 >= total * 99) e99 = (int)(i + 1);
}
LOG_INF("layer %2d: 50%%=top-%d 80%%=top-%d 90%%=top-%d 95%%=top-%d 99%%=top-%d\n",
il, e50, e80, e90, e95, e99);
}

// Emit JSON profile to file (for runtime hot-expert pinning).
// Set DS4_PROFILE_JSON_OUT=path.json to enable.
if (const char * out_path = std::getenv("DS4_PROFILE_JSON_OUT")) {
FILE * fp = std::fopen(out_path, "w");
if (fp) {
std::fprintf(fp, "{\n");
std::fprintf(fp, " \"n_layer\": %d,\n", prof.n_layer);
std::fprintf(fp, " \"n_expert\": %d,\n", prof.n_expert);
std::fprintf(fp, " \"total_picks\": %" PRIu64 ",\n", prof.total_token_picks);
std::fprintf(fp, " \"layers\": {\n");
bool first_layer = true;
for (auto & [il, hist] : prof.per_layer) {
if (hist.empty()) continue;
if (!first_layer) std::fprintf(fp, ",\n");
first_layer = false;
// Sort experts by frequency descending; emit pairs.
std::vector<std::pair<int, uint64_t>> sorted;
sorted.reserve(hist.size());
for (size_t e = 0; e < hist.size(); ++e) {
if (hist[e] > 0) sorted.emplace_back((int)e, hist[e]);
}
std::sort(sorted.begin(), sorted.end(), [](auto & a, auto & b) {
return a.second > b.second;
});
std::fprintf(fp, " \"%d\": [", il);
for (size_t i = 0; i < sorted.size(); ++i) {
if (i) std::fprintf(fp, ",");
std::fprintf(fp, "[%d,%" PRIu64 "]", sorted[i].first, sorted[i].second);
}
std::fprintf(fp, "]");
}
std::fprintf(fp, "\n }\n}\n");
std::fclose(fp);
LOG_INF("\nds4-expert-profile: wrote JSON to %s\n", out_path);
} else {
LOG_ERR("ds4-expert-profile: could not open %s\n", out_path);
}
}

llama_backend_free();
return 0;
}
4 changes: 2 additions & 2 deletions ggml/include/ggml-rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ extern "C" {

#define RPC_PROTO_MAJOR_VERSION 4
#define RPC_PROTO_MINOR_VERSION 0
#define RPC_PROTO_PATCH_VERSION 0
#define RPC_PROTO_PATCH_VERSION 1

#ifdef __cplusplus
static_assert(GGML_OP_COUNT == 96, "GGML_OP_COUNT has changed - update RPC_PROTO_PATCH_VERSION");
static_assert(GGML_OP_COUNT == 97, "GGML_OP_COUNT has changed - update RPC_PROTO_PATCH_VERSION");
#endif

#define GGML_RPC_MAX_SERVERS 16
Expand Down
26 changes: 25 additions & 1 deletion ggml/include/ggml.h
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,8 @@ extern "C" {
GGML_TYPE_MXFP4 = 39, // MXFP4 (1 block)
GGML_TYPE_NVFP4 = 40, // NVFP4 (4 blocks, E4M3 scale)
GGML_TYPE_Q1_0 = 41,
GGML_TYPE_COUNT = 42,
GGML_TYPE_F8_E4M3_B128 = 42, // E4M3 FP8 values with one E8M0 scale per 128 values
GGML_TYPE_COUNT = 43,
};

// precision
Expand Down Expand Up @@ -467,6 +468,7 @@ extern "C" {
GGML_FTYPE_MOSTLY_MXFP4 = 25, // except 1d tensors
GGML_FTYPE_MOSTLY_NVFP4 = 26, // except 1d tensors
GGML_FTYPE_MOSTLY_Q1_0 = 27, // except 1d tensors
GGML_FTYPE_MOSTLY_F8_E4M3_MXFP4 = 28, // except 1d tensors
};

// available tensor operations:
Expand Down Expand Up @@ -576,6 +578,7 @@ extern "C" {
GGML_OP_OPT_STEP_SGD,

GGML_OP_GLU,
GGML_OP_HC_WEIGHTED_SUM,

GGML_OP_COUNT,
};
Expand Down Expand Up @@ -603,6 +606,9 @@ extern "C" {
GGML_UNARY_OP_CEIL,
GGML_UNARY_OP_ROUND,
GGML_UNARY_OP_TRUNC,
GGML_UNARY_OP_FP4_ACT_QUANT,
GGML_UNARY_OP_FP8_ACT_QUANT,
GGML_UNARY_OP_SINKHORN_4X4,

GGML_UNARY_OP_COUNT,
};
Expand Down Expand Up @@ -1246,7 +1252,18 @@ extern "C" {
struct ggml_context * ctx,
struct ggml_tensor * a);

// Blockwise activation quant-dequant simulation used by DeepSeek4 QAT paths.
GGML_API struct ggml_tensor * ggml_fp4_act_quant(
struct ggml_context * ctx,
struct ggml_tensor * a);

GGML_API struct ggml_tensor * ggml_fp8_act_quant(
struct ggml_context * ctx,
struct ggml_tensor * a);

GGML_API struct ggml_tensor * ggml_sinkhorn_4x4(
struct ggml_context * ctx,
struct ggml_tensor * a);

// xIELU activation function
// x = x * (c_a(alpha_n) + c_b(alpha_p, beta) * sigmoid(beta * x)) + eps * (x > 0)
Expand Down Expand Up @@ -1413,6 +1430,13 @@ extern "C" {
struct ggml_tensor * a,
struct ggml_tensor * b);

// weighted sum over the HC dimension:
// a: [n_embd, hc_mult], b: [hc_mult] => result: [n_embd]
GGML_API struct ggml_tensor * ggml_hc_weighted_sum(
struct ggml_context * ctx,
struct ggml_tensor * a,
struct ggml_tensor * b);

// change the precision of a matrix multiplication
// set to GGML_PREC_F32 for higher precision (useful for phi-2)
GGML_API void ggml_mul_mat_set_prec(
Expand Down
10 changes: 9 additions & 1 deletion ggml/src/ggml-backend-meta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,12 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(co
return handle_generic(src_ss, /*scalar_only =*/ false);
};

auto handle_hc_weighted_sum = [&](const std::vector<ggml_backend_meta_split_state> & src_ss) -> ggml_backend_meta_split_state {
GGML_ASSERT(src_ss[1].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
GGML_ASSERT(src_ss[0].axis != GGML_BACKEND_SPLIT_AXIS_1);
return src_ss[0];
};

auto handle_concat = [&](const std::vector<ggml_backend_meta_split_state> & src_ss) -> ggml_backend_meta_split_state {
const ggml_backend_meta_split_axis concat_axis = ggml_backend_meta_split_axis(ggml_get_op_params_i32(tensor, 0));
if (src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED && src_ss[1].axis >= 0 && src_ss[1].axis < GGML_MAX_DIMS) {
Expand Down Expand Up @@ -957,6 +963,9 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(co
case GGML_OP_GATED_DELTA_NET: {
split_state = handle_gated_delta_net(src_ss);
} break;
case GGML_OP_HC_WEIGHTED_SUM: {
split_state = handle_hc_weighted_sum(src_ss);
} break;
case GGML_OP_UNARY: {
split_state = handle_generic(src_ss, /*scalar_only =*/ false);
} break;
Expand Down Expand Up @@ -2123,4 +2132,3 @@ ggml_backend_t ggml_backend_meta_simple_backend(ggml_backend_t meta_backend, siz
const ggml_backend_meta_context * backend_ctx = (const ggml_backend_meta_context *) meta_backend->context;
return backend_ctx->backend_configs[index].backend;
}

Loading