metal : use function constant for RMS_NORM + SCALE

Replaces the runtime use_scale karg with a Metal function constant. The
norm+mul kernel is compiled with FC_norm_use_scale=false for MUL fusion and
FC_norm_use_scale=true for SCALE fusion, so the fused kernel has no runtime
branch.

Assisted-by: pi:llama.cpp/DeepSeek-V4-Flash-Vision-Exp
This commit is contained in:
Georgi Gerganov
2026-09-15 15:17:50 +03:00
parent 719438a1ad
commit 8dbc7c5b6a
5 changed files with 53 additions and 7 deletions
+42 -1
View File
@@ -1983,7 +1983,48 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_norm(ggml_metal_
ggml_metal_pipeline_with_params res = ggml_metal_library_get_pipeline(lib, name);
if (!res.pipeline) {
res = ggml_metal_library_compile_pipeline(lib, base, name, nullptr);
ggml_metal_cv_t cv = ggml_metal_cv_init();
ggml_metal_cv_set_bool(cv, false, FC_NORM + 0);
res = ggml_metal_library_compile_pipeline(lib, base, name, cv);
ggml_metal_cv_free(cv);
}
res.smem = 32*sizeof(float);
return res;
}
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_norm_scale(ggml_metal_library_t lib, const ggml_tensor * op) {
assert(op->op == GGML_OP_NORM || op->op == GGML_OP_RMS_NORM);
GGML_ASSERT(ggml_is_contiguous_rows(op->src[0]));
char base[256];
char name[256];
const char * suffix = "";
if (op->ne[0] % 4 == 0) {
suffix = "_4";
}
switch (op->op) {
case GGML_OP_NORM: snprintf(base, 256, "kernel_norm_mul_f32%s", suffix); break;
case GGML_OP_RMS_NORM: snprintf(base, 256, "kernel_rms_norm_mul_f32%s", suffix); break;
default: GGML_ABORT("fatal error");
}
snprintf(name, 256, "%s_use_scale", base);
ggml_metal_pipeline_with_params res = ggml_metal_library_get_pipeline(lib, name);
if (!res.pipeline) {
ggml_metal_cv_t cv = ggml_metal_cv_init();
ggml_metal_cv_set_bool(cv, true, FC_NORM + 0);
res = ggml_metal_library_compile_pipeline(lib, base, name, cv);
ggml_metal_cv_free(cv);
}
res.smem = 32*sizeof(float);
+1
View File
@@ -154,6 +154,7 @@ struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_bin_one
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_l2_norm (ggml_metal_library_t lib, const struct ggml_tensor * op);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_group_norm (ggml_metal_library_t lib, const struct ggml_tensor * op);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_norm (ggml_metal_library_t lib, const struct ggml_tensor * op, int32_t n_fuse);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_norm_scale (ggml_metal_library_t lib, const struct ggml_tensor * op);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_rope (ggml_metal_library_t lib, const struct ggml_tensor * op);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_im2col (ggml_metal_library_t lib, const struct ggml_tensor * op);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_conv_transpose_1d (ggml_metal_library_t lib, const struct ggml_tensor * op);
+1 -1
View File
@@ -114,6 +114,7 @@
#define FC_SUM_ROWS 1400
#define FC_UPSCALE 1500
#define FC_GATED_DELTA_NET 1600
#define FC_NORM 1700
// op-specific constants
#define OP_FLASH_ATTN_EXT_NQPSG 8
@@ -613,7 +614,6 @@ typedef struct {
uint64_t nbf2[3];
uint64_t nbf3[3];
float scale_val;
int32_t use_scale;
} ggml_metal_kargs_norm;
typedef struct {
+5 -3
View File
@@ -4086,10 +4086,10 @@ int ggml_metal_op_norm(ggml_metal_op_t ctx, int idx) {
/*.nbf2 =*/ { nb02 },
/*.nbf3 =*/ { nb03 },
/*.scale_val =*/ 1.0f,
/*.use_scale =*/ 0,
};
int n_fuse = 1;
bool fused_norm_scale = false;
ggml_metal_buffer_id bid_fuse[2] = { bid_src0, bid_src0 };
@@ -4131,12 +4131,12 @@ int ggml_metal_op_norm(ggml_metal_op_t ctx, int idx) {
if (fusion && fusion->id == GGML_METAL_FUSION_NORM_SCALE) {
n_fuse = n;
fused_norm_scale = true;
ctx->count_fusions(fusion);
const ggml_tensor * scale_node = ctx->node(idx + 1);
args.scale_val = ggml_get_op_params_f32(scale_node, 0);
args.use_scale = 1;
if (debug_fusion > 1) {
GGML_LOG_DEBUG("%s: fuse: %s + SCALE\n", __func__, ggml_op_name(op->op));
@@ -4156,7 +4156,9 @@ int ggml_metal_op_norm(ggml_metal_op_t ctx, int idx) {
}
}
auto pipeline = ggml_metal_library_get_pipeline_norm(lib, op, n_fuse);
auto pipeline = fused_norm_scale ?
ggml_metal_library_get_pipeline_norm_scale(lib, op) :
ggml_metal_library_get_pipeline_norm(lib, op, n_fuse);
int nth = 32; // SIMD width
+4 -2
View File
@@ -1,5 +1,7 @@
#include "common.h"
constant bool FC_norm_use_scale [[function_constant(FC_NORM + 0)]];
// F == 1 : norm (no fuse)
// F == 2 : norm + mul
// F == 3 : norm + mul + add
@@ -80,7 +82,7 @@ kernel void kernel_norm_fuse_impl(
y[i00] = (y[i00]*scale);
}
if (F == 2) {
if (args.use_scale) {
if (FC_norm_use_scale) {
y[i00] = (y[i00]*scale) * args.scale_val;
} else {
y[i00] = (y[i00]*scale)*f0[i00];
@@ -159,7 +161,7 @@ kernel void kernel_rms_norm_fuse_impl(
y[i00] = (x[i00]*scale);
}
if (F == 2) {
if (args.use_scale) {
if (FC_norm_use_scale) {
y[i00] = (x[i00]*scale) * args.scale_val;
} else {
y[i00] = (x[i00]*scale)*f0[i00];