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:
@@ -2,6 +2,61 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// mul_mat(gate) + mul_mat(up) + GLU: graph shape and tensor properties only. Backend state
|
||||
// (weight layout, split buffers, DMMV) is checked by ggml_sycl_mul_mat_glu_mmvq_fused().
|
||||
static bool ggml_sycl_should_fuse_mul_mat_glu(const ggml_tensor * gate, const ggml_tensor * up,
|
||||
const ggml_tensor * glu) {
|
||||
// the fused epilogue implements these two; the rest fall back to the standalone GLU kernels
|
||||
const ggml_glu_op glu_op = ggml_get_glu_op(glu);
|
||||
if (glu_op != GGML_GLU_OP_SWIGLU && glu_op != GGML_GLU_OP_GEGLU) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the kernel always treats src[0] as the activated operand and src[1] as the multiplier
|
||||
if (ggml_get_op_params_i32(glu, 1) /* swapped */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ggml_tensor * wu = up->src[0];
|
||||
const ggml_tensor * wg = gate->src[0];
|
||||
const ggml_tensor * act = up->src[1];
|
||||
|
||||
// one set of block offsets and one quantized activation must serve both weights
|
||||
if (wu->type != wg->type || !ggml_are_same_shape(wu, wg) || !ggml_are_same_stride(wu, wg)) {
|
||||
return false;
|
||||
}
|
||||
if (act != gate->src[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only q4_K has a fused reorder GEMV so far, and it walks whole super-blocks
|
||||
if (wu->type != GGML_TYPE_Q4_K || wu->ne[0] % QK_K != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// one 2D reorder-layout matrix in, a plain column stride out: no broadcast or padding
|
||||
if (!ggml_is_contiguous(wu) || !ggml_is_contiguous(wg) || !ggml_is_contiguous(act) ||
|
||||
!ggml_is_contiguous(glu)) {
|
||||
return false;
|
||||
}
|
||||
if (act->type != GGML_TYPE_F32 || glu->type != GGML_TYPE_F32) {
|
||||
return false;
|
||||
}
|
||||
if (act->ne[2] != 1 || act->ne[3] != 1 || wu->ne[2] != 1 || wu->ne[3] != 1) {
|
||||
return false;
|
||||
}
|
||||
// the kernel writes rows [0, wu->ne[1]) of each glu column, strided by glu->ne[0]
|
||||
if (glu->ne[0] != wu->ne[1] || glu->ne[1] != act->ne[1]) {
|
||||
return false;
|
||||
}
|
||||
// mat-vec only: one column per decoded token, up to the batch the reorder kernels cover
|
||||
if (act->ne[1] > MMVQ_MAX_BATCH_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ggml_sycl_can_fuse(const ggml_cgraph * cgraph, int node_idx, std::initializer_list<enum ggml_op> ops,
|
||||
std::initializer_list<enum ggml_unary_op> unary_ops) {
|
||||
#ifndef NDEBUG
|
||||
@@ -13,6 +68,28 @@ bool ggml_sycl_can_fuse(const ggml_cgraph * cgraph, int node_idx, std::initializ
|
||||
return false;
|
||||
}
|
||||
|
||||
// gate and up are siblings, not a chain, so ggml_can_fuse cannot express this: use the
|
||||
// subgraph form with the GLU as the only materialised output.
|
||||
if (ops.size() == 3 && ops.begin()[0] == GGML_OP_MUL_MAT && ops.begin()[1] == GGML_OP_MUL_MAT &&
|
||||
ops.begin()[2] == GGML_OP_GLU) {
|
||||
if (!ggml_can_fuse_subgraph(cgraph, node_idx, ops, { node_idx + 2 })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ggml_tensor * glu = cgraph->nodes[node_idx + 2];
|
||||
const ggml_tensor * gate = glu->src[0];
|
||||
const ggml_tensor * up = glu->src[1];
|
||||
|
||||
// don't assume which of the two mat-muls is the gate; infer it from the GLU's operands
|
||||
const bool ok = (gate == cgraph->nodes[node_idx] && up == cgraph->nodes[node_idx + 1]) ||
|
||||
(gate == cgraph->nodes[node_idx + 1] && up == cgraph->nodes[node_idx]);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ggml_sycl_should_fuse_mul_mat_glu(gate, up, glu);
|
||||
}
|
||||
|
||||
if (!ggml_can_fuse(cgraph, node_idx, ops)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user