Skip to content

Commit 1a91884

Browse files
committed
dflash: fixed-shape masked decoder (wip p2b kv-cache foundation)
masking verified correct (80% accept, greedy-equivalent) but slower than the 472 t/s baseline until the per-layer cross-K/V cache lands - see dflash.md P2b.
1 parent 52e1072 commit 1a91884

2 files changed

Lines changed: 53 additions & 12 deletions

File tree

src/llama-context.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,15 +1383,45 @@ llm_graph_result * llama_context::process_ubatch(const llama_ubatch & ubatch, ll
13831383
// FIXME this call causes a crash if any model inputs were not used in the graph and were therefore not allocated
13841384
res->set_inputs(&ubatch);
13851385

1386-
// DFlash decoder positions span [target_ctx ; noise] = arange(n_enc + n_noise)
1386+
// DFlash decoder: fill the fixed-size injected context, RoPE positions, and the padding
1387+
// mask. target_ctx is [n_embd, n_ctx_max]; only the first n_valid columns are real context,
1388+
// the noise block occupies the last ubatch.n_tokens key slots.
13871389
if (dflash_decoder_ctx && !cross.v_embd.empty()) {
1390+
const int64_t n_embd = model.hparams.n_embd;
1391+
const int64_t n_ctx = cparams.n_ctx;
1392+
const int64_t n_valid = std::min<int64_t>(cross.n_enc, n_ctx);
1393+
const int64_t n_block = ubatch.n_tokens;
1394+
const int64_t n_kv = n_ctx + n_block;
1395+
1396+
if (ggml_tensor * tctx = ggml_graph_get_tensor(gf, "dflash_target_ctx")) {
1397+
std::vector<float> buf((size_t) n_embd * n_ctx, 0.0f);
1398+
std::memcpy(buf.data(), cross.v_embd.data(), (size_t) n_embd * n_valid * sizeof(float));
1399+
ggml_backend_tensor_set(tctx, buf.data(), 0, buf.size() * sizeof(float));
1400+
}
13881401
if (ggml_tensor * pos_full = ggml_graph_get_tensor(gf, "inp_pos_full")) {
1389-
const int64_t n_total = cross.n_enc + ubatch.n_tokens;
1390-
std::vector<int32_t> pos_data(n_total);
1391-
for (int64_t i = 0; i < n_total; ++i) {
1392-
pos_data[i] = (int32_t) i;
1402+
std::vector<int32_t> pos(n_kv, 0);
1403+
for (int64_t i = 0; i < n_valid; ++i) {
1404+
pos[i] = (int32_t) i;
1405+
}
1406+
for (int64_t j = 0; j < n_block; ++j) {
1407+
pos[n_ctx + j] = (int32_t) (n_valid + j);
1408+
}
1409+
ggml_backend_tensor_set(pos_full, pos.data(), 0, pos.size() * sizeof(int32_t));
1410+
}
1411+
if (ggml_tensor * mask = ggml_graph_get_tensor(gf, "dflash_kq_mask")) {
1412+
std::vector<float> m((size_t) n_kv * n_block, 0.0f);
1413+
for (int64_t q = 0; q < n_block; ++q) {
1414+
for (int64_t k = n_valid; k < n_ctx; ++k) {
1415+
m[(size_t) q * n_kv + k] = -INFINITY;
1416+
}
1417+
}
1418+
if (mask->type == GGML_TYPE_F16) {
1419+
std::vector<ggml_fp16_t> mh(m.size());
1420+
ggml_fp32_to_fp16_row(m.data(), mh.data(), m.size());
1421+
ggml_backend_tensor_set(mask, mh.data(), 0, mh.size() * sizeof(ggml_fp16_t));
1422+
} else {
1423+
ggml_backend_tensor_set(mask, m.data(), 0, m.size() * sizeof(float));
13931424
}
1394-
ggml_backend_tensor_set(pos_full, pos_data.data(), 0, n_total * sizeof(int32_t));
13951425
}
13961426
}
13971427

src/models/dflash.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,13 @@ llama_model_dflash::graph<false>::graph(const llama_model & model, const llm_gra
148148
cb(noise_embd, "inp_noise_embd", -1);
149149
res->add_input(std::move(inp));
150150

151-
// injected target context: [n_embd, n_enc] from llama_cross::v_embd
152-
ggml_tensor * target_ctx = build_inp_cross_embd();
153-
const int64_t n_ctx = target_ctx->ne[1];
151+
// P2b step A: fixed-size injected target context [n_embd, n_ctx_max] so the decoder graph
152+
// shape is constant across draft steps (enables graph reuse + CUDA graphs). The active context
153+
// length varies per step; padded rows are masked out by dflash_kq_mask. Filled by cb-name.
154+
const int64_t n_ctx = cparams.n_ctx;
155+
ggml_tensor * target_ctx = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_embd, n_ctx);
156+
ggml_set_input(target_ctx);
157+
cb(target_ctx, "dflash_target_ctx", -1);
154158

155159
const int64_t n_tokens_kv = n_ctx + n_tokens;
156160

@@ -162,6 +166,14 @@ llama_model_dflash::graph<false>::graph(const llama_model & model, const llm_gra
162166
// query positions = the noise block (last n_tokens entries)
163167
ggml_tensor * inp_pos_q = ggml_view_1d(ctx0, inp_pos_full, n_tokens, n_ctx * ggml_element_size(inp_pos_full));
164168

169+
// additive attention mask [n_kv, n_q]: 0 = attend, -INF = masked. Masks the padded context
170+
// rows [n_valid, n_ctx); valid context + the noise block are visible. Filled by cb-name.
171+
// flash attention requires an F16 mask; the non-flash soft-max path takes F32
172+
ggml_tensor * kq_mask = ggml_new_tensor_4d(ctx0,
173+
cparams.flash_attn ? GGML_TYPE_F16 : GGML_TYPE_F32, n_tokens_kv, n_tokens, 1, 1);
174+
ggml_set_input(kq_mask);
175+
cb(kq_mask, "dflash_kq_mask", -1);
176+
165177
const float kq_scale = 1.0f/sqrtf(float(n_embd_head));
166178

167179
ggml_tensor * inpL = noise_embd;
@@ -206,9 +218,8 @@ llama_model_dflash::graph<false>::graph(const llama_model & model, const llm_gra
206218
ext_factor, attn_factor, beta_fast, beta_slow);
207219
cb(Qcur, "Qcur_rope", il);
208220

209-
// single-block baseline: full attention (no causal mask). The block-diffusion mask
210-
// (causal-to-anchor for context, bidirectional within block) is added with np>1.
211-
ggml_tensor * cur = build_attn_mha(Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, nullptr, kq_scale, il);
221+
// attention with the padded-context mask (see dflash_kq_mask above)
222+
ggml_tensor * cur = build_attn_mha(Qcur, Kcur, Vcur, nullptr, kq_mask, nullptr, nullptr, kq_scale, il);
212223
cb(cur, "kqv_out", il);
213224

214225
cur = build_lora_mm(layer.wo, cur);

0 commit comments

Comments
 (0)