sycl: fuse mul_mat(gate) + mul_mat(up) + GLU for q4_K dense FFN (#26779)
Measured on Arc Pro B70 (Battlemage, Level Zero), llama-bench -r 20, two
interleaved rounds, tg128:
qwen2.5-3B-Instruct Q4_K_M 154.18 -> 158.53 t/s +2.8%
gemma-2-2b-it Q4_K_M 162.45 -> 165.62 t/s +2.0%
llama-batched-bench on qwen2.5-3B, S_TG by batch size:
B=1 142.72 -> 147.57 t/s +3.4%
B=2 243.72 -> 268.26 t/s +10.1%
B=4 359.58 -> 398.02 t/s +10.7%
B=8 449.75 -> 505.63 t/s +12.4%
This commit is contained in:
@@ -4561,6 +4561,66 @@ static void ggml_sycl_mul_mat(ggml_backend_sycl_context & ctx, const ggml_tensor
|
||||
}
|
||||
}
|
||||
|
||||
// Fused dense-FFN mat-vec for the {mul_mat(gate), mul_mat(up), GLU} subgraph at node_idx.
|
||||
// Returns false if it declined, in which case the caller runs the three nodes normally.
|
||||
static bool ggml_sycl_mul_mat_glu_mmvq_fused(ggml_backend_sycl_context & ctx, ggml_cgraph * cgraph, int node_idx) {
|
||||
if (!ggml_sycl_can_fuse(cgraph, node_idx, { GGML_OP_MUL_MAT, GGML_OP_MUL_MAT, GGML_OP_GLU }, {})) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ggml_tensor * glu = cgraph->nodes[node_idx + 2];
|
||||
ggml_tensor * gate = glu->src[0];
|
||||
ggml_tensor * up = glu->src[1];
|
||||
const ggml_tensor * wu = up->src[0];
|
||||
const ggml_tensor * wg = gate->src[0];
|
||||
const ggml_tensor * act = up->src[1];
|
||||
|
||||
// this writes glu->data directly rather than the per-device row slices that
|
||||
// ggml_sycl_op_mul_mat() stitches back together, so it cannot serve split weights
|
||||
if (ggml_backend_buffer_is_sycl_split(wu->buffer) || ggml_backend_buffer_is_sycl_split(wg->buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// with DMMV prioritised the unfused path would not have gone through mmvq at all
|
||||
if (g_ggml_sycl_prioritize_dmmv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// install the reorder (SoA) layout the fused kernel needs, as the unfused mmvq path would;
|
||||
// a no-op once done. after the bail checks so a declined op does not pay for it.
|
||||
opt_for_reorder(&ctx, wu, act, up, mul_mat_algo::MMVQ);
|
||||
opt_for_reorder(&ctx, wg, act, gate, mul_mat_algo::MMVQ);
|
||||
|
||||
const auto * extra_u = static_cast<const ggml_tensor_extra_gpu *>(wu->extra);
|
||||
const auto * extra_g = static_cast<const ggml_tensor_extra_gpu *>(wg->extra);
|
||||
if (!extra_u || !extra_g || !extra_u->optimized_feature.reorder || !extra_g->optimized_feature.reorder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// log the up mat-mul: glu's own srcs are the two intermediates the fusion never materialises
|
||||
scope_op_debug_print scope_dbg_print(__func__, up, /*num_src=*/2, " : fused with gate + GLU");
|
||||
|
||||
const int64_t ne00 = wu->ne[0];
|
||||
const int64_t ne11 = act->ne[1];
|
||||
|
||||
const queue_ptr stream = ctx.stream();
|
||||
const int src1_padded_cols = GGML_PAD((int) ne00, MATRIX_ROW_PADDING);
|
||||
|
||||
// one activation, quantized once and fully consumed into src1_ddq before the GEMV on this
|
||||
// in-order queue, so glu->data aliasing the dead activation needs no memory-range check
|
||||
ggml_sycl_pool_alloc<char> src1_q8_alloc(ctx.pool(),
|
||||
(size_t) ne11 * src1_padded_cols * sizeof(block_q8_1) / QK8_1);
|
||||
char * src1_ddq = src1_q8_alloc.get();
|
||||
|
||||
quantize_row_q8_1_sycl<quantize_and_reorder_q8_1_soa>((const float *) act->data, src1_ddq, (int) ne00, (int) ne11,
|
||||
src1_padded_cols, stream);
|
||||
|
||||
return ggml_sycl_mul_mat_vec_q_glu_reorder(wu->type, ggml_get_glu_op(glu), wu->data, wg->data, src1_ddq,
|
||||
(float *) glu->data, (int) ne00, (int) wu->ne[1], (int) ne11,
|
||||
/*stride_col_y_bytes=*/src1_padded_cols * (int) sizeof(block_q8_1) /
|
||||
QK8_1,
|
||||
/*stride_col_dst=*/(int) glu->ne[0], stream);
|
||||
}
|
||||
|
||||
__dpct_inline__ static void k_copy_src1_to_contiguous(
|
||||
const char *__restrict__ src1_original, char *__restrict__ src1_contiguous,
|
||||
@@ -5591,6 +5651,11 @@ static void ggml_backend_sycl_graph_compute_impl(ggml_backend_sycl_context * syc
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node->op == GGML_OP_MUL_MAT && ggml_sycl_mul_mat_glu_mmvq_fused(*sycl_ctx, cgraph, i)) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = ggml_sycl_compute_forward(*sycl_ctx, node);
|
||||
if (!ok) {
|
||||
GGML_LOG_ERROR("%s: error: op not supported %s (%s)\n", __func__, node->name, ggml_op_name(node->op));
|
||||
|
||||
Reference in New Issue
Block a user