Skip to content

Commit 471fb4e

Browse files
authored
Merge pull request #220 from TheTom/tom/pr-219-full-rebase
dflash: port #219 onto feature (rebased full CL)
2 parents f272689 + 258e1f9 commit 471fb4e

18 files changed

Lines changed: 491 additions & 341 deletions

common/arg.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3600,6 +3600,27 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
36003600
}
36013601
}
36023602
).set_spec().set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_SPEC_DRAFT_N_CPU_MOE"));
3603+
add_opt(common_arg(
3604+
{"--eagle3"},
3605+
"use EAGLE3 speculative decoding with the draft model",
3606+
[](common_params & params) {
3607+
params.speculative.draft.eagle3 = true;
3608+
}
3609+
).set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}));
3610+
add_opt(common_arg(
3611+
{"--dflash"},
3612+
"use DFlash speculative decoding with the draft model",
3613+
[](common_params & params) {
3614+
params.speculative.draft.dflash = true;
3615+
}
3616+
).set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}));
3617+
add_opt(common_arg(
3618+
{"-cd", "--ctx-size-draft"}, "N",
3619+
string_format("size of the prompt context for the draft model (default: %d, 0 = loaded from model)", params.speculative.draft.n_ctx),
3620+
[](common_params & params, int value) {
3621+
params.speculative.draft.n_ctx = value;
3622+
}
3623+
).set_spec().set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_SPEC_DRAFT_N_CTX"));
36033624

36043625
add_opt(common_arg(
36053626
{"--spec-draft-n-max"}, "N",

common/common.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,11 @@ struct common_params_speculative_draft {
325325
std::vector<ggml_backend_dev_t> devices; // devices to use for offloading
326326

327327
std::vector<llama_model_tensor_buft_override> tensor_buft_overrides;
328+
329+
bool eagle3 = false; // use EAGLE3 speculative decoding
330+
bool dflash = false; // use DFlash speculative decoding
331+
int32_t n_ctx = 0; // draft context size
332+
328333
};
329334

330335
struct common_params_speculative_ngram_mod {

common/speculative.cpp

Lines changed: 90 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,14 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
835835

836836
// scratch buffer for concatenated target features [n_tokens, n_embd_enc]
837837
std::vector<float> features_buf;
838+
// Stashed encoder outputs for deferred KV cache injection
839+
struct StashedG {
840+
llama_pos pos;
841+
std::vector<float> data; // n_embd_dec floats
842+
};
843+
std::vector<std::vector<StashedG>> stashed; // [n_seq][...]
844+
static constexpr int32_t MAX_STASH = 64;
845+
bool m_use_deferred = true;
838846

839847
common_speculative_impl_draft_dflash(const common_params_speculative & params, uint32_t n_seq)
840848
: common_speculative_impl(COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH, n_seq)
@@ -869,7 +877,17 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
869877
LOG_INF("%s: - n_max=%d, n_min=%d, p_min=%.2f\n", __func__, this->params.n_max, this->params.n_min, this->params.p_min);
870878
LOG_INF("%s: - block_size=%d, mask_token_id=%d, n_extract=%u\n", __func__, block_size, mask_token_id, target_layer_ids_n);
871879

872-
// DFlash input is [id_last, <mask> * (block_size-1)], so it can draft at most block_size-1 tokens per step
880+
// Detect target model type: Gemma4 models regress with deferred injection (low acceptance rate)
881+
{
882+
char model_desc[128] = {};
883+
llama_model_desc(model_tgt, model_desc, sizeof(model_desc));
884+
if (strstr(model_desc, "gemma")) {
885+
m_use_deferred = false;
886+
LOG_INF("%s: - deferred_kv_injection=0 (disabled for %s)\n", __func__, model_desc);
887+
} else {
888+
LOG_INF("%s: - deferred_kv_injection=1 (enabled for %s)\n", __func__, model_desc);
889+
}
890+
}
873891
if (this->params.n_max > block_size - 1 || this->params.n_min > block_size - 1) {
874892
LOG_WRN("%s: requested draft size (n_max=%d, n_min=%d) exceeds the trained DFlash block size %d -- clamping to %d\n",
875893
__func__, this->params.n_max, this->params.n_min, block_size, block_size - 1);
@@ -880,6 +898,8 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
880898
batch = llama_batch_init(llama_n_batch(ctx_dft), 0, n_seq);
881899
batch_inject = llama_batch_init(llama_n_batch(ctx_dft), n_embd_dec, n_seq);
882900

901+
stashed.resize(n_seq);
902+
883903
smpls.resize(n_seq);
884904
for (auto & s : smpls) {
885905
common_params_sampling sparams;
@@ -889,13 +909,13 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
889909
s.reset(common_sampler_init(model_dft, sparams));
890910
}
891911

892-
// turn on extraction of the target layers' input embeddings
912+
// Enable extraction of target model layer outputs for DFlash encoder
893913
for (uint32_t k = 0; k < target_layer_ids_n; ++k) {
894914
llama_set_embeddings_layer_inp(ctx_tgt, (uint32_t) target_layer_ids[k], true);
895915
}
896916

917+
// Enable nextn (encoder) output on the draft context for DFlash decoder K/V injection
897918
llama_set_embeddings_nextn(ctx_dft, true, /*masked*/ true);
898-
llama_set_causal_attn(ctx_dft, false); // DFlash needs non-causal attention
899919
}
900920

901921
~common_speculative_impl_draft_dflash() override {
@@ -996,31 +1016,76 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
9961016
const float * inp_g = llama_get_embeddings_nextn(ctx_dft);
9971017
GGML_ASSERT(inp_g && "DFlash encoder produced no output.");
9981018

999-
// inject the DFlash decoder K/V cache at the tokens' target positions
1000-
batch_inject.n_tokens = n_chunk;
1001-
std::memcpy(batch_inject.embd, inp_g, (size_t) n_chunk * n_embd_dec * sizeof(float));
1002-
1003-
for (int32_t i = 0; i < n_chunk; ++i) {
1004-
batch_inject.pos[i] = batch_in.pos[i_batch_beg[seq_id] + offset + i];
1005-
batch_inject.n_seq_id[i] = 1;
1006-
batch_inject.seq_id[i][0] = seq_id;
1007-
batch_inject.logits[i] = false;
1008-
}
1009-
rc = llama_decode(ctx_dft, batch_inject);
1010-
if (rc != 0) {
1011-
LOG_ERR("%s: llama_decode(ctx_dft) failed rc=%d (n_tokens=%d, offset=%d)\n",
1012-
__func__, rc, (int) n_chunk, (int) offset);
1013-
return false;
1019+
if (m_use_deferred) {
1020+
// stash encoder output for deferred KV cache injection
1021+
auto & stash = stashed[seq_id];
1022+
for (int32_t i = 0; i < n_chunk; ++i) {
1023+
StashedG sg;
1024+
sg.pos = batch_in.pos[i_batch_beg[seq_id] + offset + i];
1025+
sg.data.resize(n_embd_dec);
1026+
std::copy(inp_g + (size_t) i * n_embd_dec, inp_g + (size_t) (i + 1) * n_embd_dec, sg.data.begin());
1027+
stash.push_back(std::move(sg));
1028+
}
1029+
// auto-flush to prevent unbounded accumulation
1030+
if ((int32_t) stash.size() >= MAX_STASH) {
1031+
flush_injection(seq_id);
1032+
}
1033+
} else {
1034+
// immediate injection (original per-chunk behavior)
1035+
auto & stash = stashed[seq_id];
1036+
for (int32_t i = 0; i < n_chunk; ++i) {
1037+
StashedG sg;
1038+
sg.pos = batch_in.pos[i_batch_beg[seq_id] + offset + i];
1039+
sg.data.resize(n_embd_dec);
1040+
std::copy(inp_g + (size_t) i * n_embd_dec, inp_g + (size_t) (i + 1) * n_embd_dec, sg.data.begin());
1041+
stash.push_back(std::move(sg));
1042+
}
1043+
flush_injection(seq_id);
10141044
}
10151045
}
10161046
}
10171047

10181048
return true;
10191049
}
10201050

1051+
// Batch-inject all stashed encoder outputs into the draft decoder's KV cache
1052+
void flush_injection(llama_seq_id seq_id) {
1053+
auto & stash = stashed[seq_id];
1054+
if (stash.empty()) return;
1055+
1056+
const int32_t n = (int32_t) stash.size();
1057+
batch_inject.n_tokens = n;
1058+
for (int32_t i = 0; i < n; ++i) {
1059+
std::memcpy(batch_inject.embd + (size_t) i * n_embd_dec,
1060+
stash[i].data.data(), (size_t) n_embd_dec * sizeof(float));
1061+
batch_inject.pos[i] = stash[i].pos;
1062+
batch_inject.n_seq_id[i] = 1;
1063+
batch_inject.seq_id[i][0] = seq_id;
1064+
batch_inject.logits[i] = false;
1065+
}
1066+
1067+
auto * ctx_dft = params.ctx_dft;
1068+
int32_t rc = llama_decode(ctx_dft, batch_inject);
1069+
if (rc != 0) {
1070+
LOG_ERR("%s: flush_injection llama_decode(ctx_dft) failed rc=%d (n_tokens=%d)\n",
1071+
__func__, rc, n);
1072+
}
1073+
1074+
stash.clear();
1075+
}
1076+
10211077
void draft(common_speculative_draft_params_vec & dparams) override {
10221078
auto & ctx_dft = params.ctx_dft;
10231079

1080+
if (m_use_deferred) {
1081+
// flush all stashed KV injections before drafting
1082+
for (llama_seq_id seq_id = 0; seq_id < (llama_seq_id) n_seq; ++seq_id) {
1083+
if (dparams[seq_id].drafting) {
1084+
flush_injection(seq_id);
1085+
}
1086+
}
1087+
}
1088+
10241089
common_batch_clear(batch);
10251090

10261091
// build one batch holding every drafting sequence's noise block into a single decode)
@@ -2082,6 +2147,13 @@ common_speculative * common_speculative_init(common_params_speculative & params,
20822147
bool has_mtp = (enabled_configs & (1u << COMMON_SPECULATIVE_TYPE_DRAFT_MTP)) && params.draft.ctx_dft != nullptr;
20832148
bool has_draft_dflash = (enabled_configs & (1u << COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH)) && params.draft.ctx_dft != nullptr;
20842149

2150+
// If --dflash or --eagle3 flags are set, enable the corresponding type
2151+
if (!has_draft_dflash && params.draft.dflash && params.draft.ctx_dft != nullptr) {
2152+
has_draft_dflash = true;
2153+
}
2154+
if (!has_draft_eagle3 && params.draft.eagle3 && params.draft.ctx_dft != nullptr) {
2155+
has_draft_eagle3 = true;
2156+
}
20852157

20862158

20872159
bool has_ngram_cache = (enabled_configs & (1u << COMMON_SPECULATIVE_TYPE_NGRAM_CACHE));

examples/speculative-simple/speculative-simple.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ int main(int argc, char ** argv) {
7575
}
7676

7777
auto cparams = common_context_params_to_llama(params_dft);
78+
// DFlash/EAGLE3 draft models may reuse the target model's embeddings/output
79+
cparams.ctx_other = ctx_tgt;
7880
ctx_dft.reset(llama_init_from_model(model_dft.get(), cparams));
7981

8082
params.speculative.draft.ctx_tgt = ctx_tgt;

0 commit comments

Comments
 (0)