Skip to content

Commit 01c6842

Browse files
committed
feat(server): add Gemma4 target-layer-split adapter
1 parent f999368 commit 01c6842

7 files changed

Lines changed: 1018 additions & 19 deletions

File tree

dflash/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ add_library(dflash_common STATIC
226226
src/gemma4/gemma4_backend.cpp
227227
src/gemma4/gemma4_daemon.cpp
228228
src/gemma4/gemma4_dflash_target.cpp
229+
src/gemma4/gemma4_layer_split_adapter.cpp
229230
src/flashprefill_q8.cpp
230231
src/kv_cache.cpp
231232
src/kv_quant.cpp

dflash/src/common/backend_factory.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "laguna_backend.h"
88
#include "qwen3_backend.h"
99
#include "gemma4_backend.h"
10+
#include "gemma4_layer_split_adapter.h"
1011
#include "layer_split_backend.h"
1112
#include "qwen35_layer_split_adapter.h"
1213

@@ -119,6 +120,22 @@ std::unique_ptr<ModelBackend> create_backend(const BackendArgs & args) {
119120
return backend;
120121

121122
} else if (arch == "gemma4") {
123+
if (args.device.is_layer_split()) {
124+
Gemma4LayerSplitAdapterConfig cfg;
125+
cfg.target_path = args.model_path;
126+
cfg.device = args.device;
127+
cfg.chunk = args.chunk;
128+
cfg.fa_window = args.fa_window;
129+
130+
auto adapter = std::make_unique<Gemma4LayerSplitAdapter>(cfg);
131+
auto backend = std::make_unique<LayerSplitBackend>(std::move(adapter));
132+
if (!backend->init()) {
133+
std::fprintf(stderr, "[backend_factory] LayerSplitBackend(gemma4) init failed\n");
134+
return nullptr;
135+
}
136+
return backend;
137+
}
138+
122139
Gemma4BackendConfig gcfg;
123140
gcfg.model_path = args.model_path;
124141
gcfg.draft_path = args.draft_path;

dflash/src/gemma4/gemma4_graph.cpp

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,181 @@ static ggml_tensor * gemma4_view_2d_slice(ggml_context * ctx, ggml_tensor * x, i
387387
(size_t)idx * x->ne[0] * x->ne[1] * ggml_element_size(x));
388388
}
389389

390+
static ggml_tensor * build_gemma4_per_layer_input(
391+
ggml_context * ctx,
392+
const Gemma4Weights & w,
393+
ggml_tensor * embed,
394+
ggml_tensor * token_ids,
395+
int n_tokens,
396+
int layer_idx) {
397+
if (!token_ids || !w.per_layer_tok_embd || !w.per_layer_model_proj ||
398+
!w.per_layer_proj_norm || w.n_embd_per_layer <= 0) {
399+
return nullptr;
400+
}
401+
const int D = w.n_embd_per_layer;
402+
const size_t elt_tok = ggml_element_size(w.per_layer_tok_embd);
403+
const size_t elt_norm = ggml_element_size(w.per_layer_proj_norm);
404+
405+
ggml_tensor * tok_embd_layer = ggml_view_2d(
406+
ctx, w.per_layer_tok_embd, D, w.n_vocab,
407+
w.per_layer_tok_embd->nb[1], (size_t)layer_idx * D * elt_tok);
408+
ggml_tensor * inp_pl = ggml_get_rows(ctx, tok_embd_layer, token_ids);
409+
inp_pl = ggml_scale(ctx, inp_pl, std::sqrt((float)D));
410+
411+
ggml_tensor * proj_w_layer = ggml_view_2d(
412+
ctx, w.per_layer_model_proj, w.n_embd, D,
413+
w.per_layer_model_proj->nb[1],
414+
(size_t)layer_idx * D * w.per_layer_model_proj->nb[1]);
415+
ggml_tensor * proj = ggml_mul_mat(ctx, proj_w_layer, embed);
416+
proj = ggml_scale(ctx, proj, 1.0f / std::sqrt((float)w.n_embd));
417+
proj = ggml_rms_norm(ctx, proj, w.norm_eps);
418+
ggml_tensor * norm_w = ggml_view_1d(
419+
ctx, w.per_layer_proj_norm, D, (size_t)layer_idx * D * elt_norm);
420+
proj = ggml_mul(ctx, proj, norm_w);
421+
422+
ggml_tensor * per_layer = ggml_add(ctx, proj, inp_pl);
423+
return ggml_scale(ctx, per_layer, 1.0f / std::sqrt(2.0f));
424+
}
425+
426+
void gemma4_layer_step_graph_free(Gemma4LayerStepGraph & sg) {
427+
if (sg.ctx) {
428+
ggml_free(sg.ctx);
429+
sg.ctx = nullptr;
430+
}
431+
sg.gf = nullptr;
432+
sg.positions = nullptr;
433+
sg.token_ids = nullptr;
434+
sg.attn_mask_full = nullptr;
435+
sg.attn_mask_swa = nullptr;
436+
}
437+
438+
void gemma4_layer_step_graph_destroy(Gemma4LayerStepGraph & sg) {
439+
if (sg.alloc) {
440+
ggml_gallocr_free(sg.alloc);
441+
sg.alloc = nullptr;
442+
}
443+
gemma4_layer_step_graph_free(sg);
444+
}
445+
446+
bool build_gemma4_layer_step(
447+
Gemma4LayerStepGraph & sg,
448+
const Gemma4Weights & w,
449+
Gemma4Cache & cache,
450+
ggml_backend_t backend,
451+
int layer_idx,
452+
ggml_tensor * act_in,
453+
ggml_tensor * orig_embed,
454+
ggml_tensor * act_out,
455+
int chunk_start,
456+
int n_tokens,
457+
int kv_start) {
458+
gemma4_layer_step_graph_free(sg);
459+
if (layer_idx < 0 || layer_idx >= w.n_layer) return false;
460+
461+
ggml_init_params ip{};
462+
ip.mem_size = ggml_tensor_overhead() * 16384 + ggml_graph_overhead() + 16 * 1024 * 1024;
463+
ip.no_alloc = true;
464+
sg.ctx = ggml_init(ip);
465+
if (!sg.ctx) return false;
466+
sg.gf = ggml_new_graph_custom(sg.ctx, 16384, false);
467+
468+
ggml_tensor * inp = ggml_view_2d(
469+
sg.ctx, act_in, w.n_embd, n_tokens,
470+
act_in->nb[1], (size_t)chunk_start * act_in->nb[1]);
471+
ggml_set_input(inp);
472+
473+
ggml_tensor * embed = ggml_view_2d(
474+
sg.ctx, orig_embed, w.n_embd, n_tokens,
475+
orig_embed->nb[1], (size_t)chunk_start * orig_embed->nb[1]);
476+
ggml_set_input(embed);
477+
478+
sg.positions = ggml_new_tensor_1d(sg.ctx, GGML_TYPE_I32, n_tokens);
479+
ggml_set_input(sg.positions);
480+
481+
sg.token_ids = ggml_new_tensor_1d(sg.ctx, GGML_TYPE_I32, n_tokens);
482+
ggml_set_input(sg.token_ids);
483+
484+
const int kv_len_raw = kv_start + n_tokens;
485+
const int kv_len_padded = (kv_len_raw + 255) & ~255;
486+
sg.attn_mask_full = ggml_new_tensor_4d(
487+
sg.ctx, GGML_TYPE_F32, kv_len_padded, n_tokens, 1, 1);
488+
ggml_set_input(sg.attn_mask_full);
489+
ggml_tensor * mask_full_f16 = ggml_cast(sg.ctx, sg.attn_mask_full, GGML_TYPE_F16);
490+
491+
const int swa_size = cache.swa_size;
492+
if (swa_size <= 0) return false;
493+
const int swa_len_raw = std::min(kv_start + n_tokens, swa_size);
494+
const int swa_len_padded = (swa_len_raw + 255) & ~255;
495+
sg.attn_mask_swa = ggml_new_tensor_4d(
496+
sg.ctx, GGML_TYPE_F32, swa_len_padded, n_tokens, 1, 1);
497+
ggml_set_input(sg.attn_mask_swa);
498+
ggml_tensor * mask_swa_f16 = ggml_cast(sg.ctx, sg.attn_mask_swa, GGML_TYPE_F16);
499+
500+
ggml_tensor * pl_input = build_gemma4_per_layer_input(
501+
sg.ctx, w, embed, sg.token_ids, n_tokens, layer_idx);
502+
ggml_tensor * layer_out = build_gemma4_layer(
503+
sg.ctx, sg.gf, w, cache, layer_idx, inp, sg.positions,
504+
mask_full_f16, mask_swa_f16, pl_input, kv_start, n_tokens);
505+
if (!layer_out) return false;
506+
507+
ggml_tensor * out_view = ggml_view_2d(
508+
sg.ctx, act_out, w.n_embd, n_tokens,
509+
act_out->nb[1], (size_t)chunk_start * act_out->nb[1]);
510+
ggml_build_forward_expand(sg.gf, ggml_cpy(sg.ctx, layer_out, out_view));
511+
512+
if (!sg.alloc) {
513+
sg.alloc = ggml_gallocr_new(ggml_backend_get_default_buffer_type(backend));
514+
}
515+
return ggml_gallocr_alloc_graph(sg.alloc, sg.gf);
516+
}
517+
518+
bool compute_gemma4_split_argmax(
519+
ggml_backend_t backend,
520+
const Gemma4Weights & w,
521+
ggml_tensor * act,
522+
int token_offset,
523+
int n_tokens,
524+
std::vector<int32_t> & out_argmax) {
525+
ggml_init_params ip{};
526+
ip.mem_size = ggml_tensor_overhead() * 64 + ggml_graph_overhead() + 1024 * 1024;
527+
ip.no_alloc = true;
528+
ggml_context * ctx = ggml_init(ip);
529+
if (!ctx) return false;
530+
ggml_cgraph * gf = ggml_new_graph(ctx);
531+
532+
ggml_tensor * act_view = ggml_view_2d(
533+
ctx, act, w.n_embd, n_tokens, act->nb[1],
534+
(size_t)token_offset * act->nb[1]);
535+
ggml_tensor * cur = gemma4_rms_norm_mul(ctx, act_view, w.out_norm, w.norm_eps);
536+
cur = ggml_mul_mat(ctx, w.output, cur);
537+
if (w.final_logit_softcap > 0.0f) {
538+
cur = ggml_scale(ctx, cur, 1.0f / w.final_logit_softcap);
539+
cur = ggml_tanh(ctx, cur);
540+
cur = ggml_scale(ctx, cur, w.final_logit_softcap);
541+
}
542+
cur = ggml_argmax(ctx, cur);
543+
ggml_set_output(cur);
544+
ggml_build_forward_expand(gf, cur);
545+
546+
ggml_gallocr_t alloc = ggml_gallocr_new(ggml_backend_get_default_buffer_type(backend));
547+
if (!alloc || !ggml_gallocr_alloc_graph(alloc, gf)) {
548+
if (alloc) ggml_gallocr_free(alloc);
549+
ggml_free(ctx);
550+
return false;
551+
}
552+
if (ggml_backend_graph_compute(backend, gf) != GGML_STATUS_SUCCESS) {
553+
ggml_gallocr_free(alloc);
554+
ggml_free(ctx);
555+
return false;
556+
}
557+
out_argmax.resize((size_t)n_tokens);
558+
ggml_backend_tensor_get(cur, out_argmax.data(), 0,
559+
sizeof(int32_t) * (size_t)n_tokens);
560+
ggml_gallocr_free(alloc);
561+
ggml_free(ctx);
562+
return true;
563+
}
564+
390565
bool gemma4_step(
391566
ggml_backend_t backend,
392567
const Gemma4Weights & w,

dflash/src/gemma4/gemma4_internal.h

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
#include <vector>
1616

1717
#include "ggml.h"
18+
#include "ggml-alloc.h"
1819
#include "ggml-backend.h"
1920

2021
#include "internal.h" // CpuEmbedder
22+
#include "common/layer_split_utils.h"
2123

2224
namespace dflash::common {
2325

@@ -146,11 +148,15 @@ inline int gemma4_n_head_kv(const Gemma4Weights & w, int il) {
146148
return w.n_head_kv;
147149
}
148150

149-
// GGUF loader
150151
bool load_gemma4_gguf(const std::string & path,
151152
ggml_backend_t backend,
152153
Gemma4Weights & out);
153154

155+
bool load_gemma4_gguf_partial(const std::string & path,
156+
ggml_backend_t backend,
157+
const LayerSplitLoadPlan & plan,
158+
Gemma4Weights & out);
159+
154160
void free_gemma4_weights(Gemma4Weights & w);
155161

156162
// KV cache
@@ -184,6 +190,12 @@ struct Gemma4Cache {
184190

185191
bool create_gemma4_cache(ggml_backend_t backend, const Gemma4Weights & w,
186192
int max_ctx, Gemma4Cache & out);
193+
bool create_gemma4_cache_partial(ggml_backend_t backend,
194+
const Gemma4Weights & w,
195+
int max_ctx,
196+
int layer_begin,
197+
int layer_end,
198+
Gemma4Cache & out);
187199
void free_gemma4_cache(Gemma4Cache & c);
188200

189201
// Allocate target_feat ring buffer (call after draft load determines n_capture_layers).
@@ -239,6 +251,41 @@ bool gemma4_project_hidden(
239251
int n_tokens,
240252
std::vector<int32_t> & out_tokens);
241253

254+
struct Gemma4LayerStepGraph {
255+
ggml_context * ctx = nullptr;
256+
ggml_cgraph * gf = nullptr;
257+
ggml_gallocr_t alloc = nullptr;
258+
259+
ggml_tensor * positions = nullptr;
260+
ggml_tensor * token_ids = nullptr;
261+
ggml_tensor * attn_mask_full = nullptr;
262+
ggml_tensor * attn_mask_swa = nullptr;
263+
};
264+
265+
void gemma4_layer_step_graph_free(Gemma4LayerStepGraph & sg);
266+
void gemma4_layer_step_graph_destroy(Gemma4LayerStepGraph & sg);
267+
268+
bool build_gemma4_layer_step(
269+
Gemma4LayerStepGraph & sg,
270+
const Gemma4Weights & w,
271+
Gemma4Cache & cache,
272+
ggml_backend_t backend,
273+
int layer_idx,
274+
ggml_tensor * act_in,
275+
ggml_tensor * orig_embed,
276+
ggml_tensor * act_out,
277+
int chunk_start,
278+
int n_tokens,
279+
int kv_start);
280+
281+
bool compute_gemma4_split_argmax(
282+
ggml_backend_t backend,
283+
const Gemma4Weights & w,
284+
ggml_tensor * act,
285+
int token_offset,
286+
int n_tokens,
287+
std::vector<int32_t> & out_argmax);
288+
242289
// BSA sparse-FA prefill: process the full prompt at once using block-sparse
243290
// attention for SWA layers (flash_prefill_forward_bf16). Full-attention layers
244291
// use dense FA. Returns logits for the last token. Populates the KV cache

0 commit comments

Comments
 (0)