Skip to content

Commit e647cd8

Browse files
iancrisclaude
andcommitted
feat: QVAC-21320 Mali GPU projector optimizations — disable FA, warptile, layernorm fusion
Three quality-neutral ggml-vulkan optimizations for the Mali-G715 vision projector (mmproj/image encoder), benchmarked on Pixel 9 Pro (Qwen3.5-0.8B): 1. Disable flash attention on GPU projectors without efficient (coopmat) FA (tools/mtmd/clip.cpp). Uses runtime proc_address resolution to query the backend — no compile-time backend dependency. Mali FA_SCALAR ~2.6x less efficient than the matmul path; coopmat-capable GPUs keep FA enabled. 2. Mali/Valhall warptile tuning (ggml-vulkan.cpp, VK_VENDOR_ID_ARM) — large q8_0 MMQ tile to 32-warp/16-wide layout; ~90->~124 GFLOPS/s. Self-disables if shared memory is insufficient. Vendor-wide: also speeds the LLM prefill on GPU. 3. NORM+MUL+ADD (layernorm) Vulkan fusion (norm.comp, generic_binary_head.glsl, vulkan-shaders-gen.cpp, ggml-vulkan.cpp) — one dispatch replaces three; mirrors rms_norm+mul. -26 dispatches/encode. NOT Mali-specific. Result (4-run CPU-matched, profiler-off): within-run GPU/CPU mmproj-encode ratio ~1.46x (baseline) -> ~1.12x (optimized); near-parity 1.09x at high resolution. Quality 37.8%==37.8% (delta=0). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e9ad5fc commit e647cd8

5 files changed

Lines changed: 218 additions & 25 deletions

File tree

ggml/src/ggml-vulkan/ggml-vulkan.cpp

Lines changed: 135 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,7 @@ struct vk_device_struct {
811811
vk_pipeline pipeline_set_rows_i32[GGML_TYPE_COUNT];
812812
vk_pipeline pipeline_set_rows_i64[GGML_TYPE_COUNT];
813813
vk_pipeline pipeline_norm_f32;
814+
vk_pipeline pipeline_norm_mul_add_f32;
814815
vk_pipeline pipeline_group_norm_f32;
815816
vk_pipeline pipeline_rms_norm_f32;
816817
vk_pipeline pipeline_rms_norm_mul_f32;
@@ -3640,6 +3641,14 @@ static void ggml_vk_load_shaders(vk_device& device) {
36403641
// Xe2/Xe3 with coopmat enabled - warptile performance tuning
36413642
l_warptile = { 512, 128, 128, 16, subgroup_size_8, 32, 2, tm_m, tn_m, tk_m, subgroup_size_8 };
36423643
l_warptile_mmq = { 512, 128, 128, 32, subgroup_size_8, 32, 2, tm_m, tn_m, tk_m, subgroup_size_8 };
3644+
} else if (device->vendor_id == VK_VENDOR_ID_ARM) {
3645+
// QVAC-21257 iter2: Mali/Valhall (16-wide subgroup, no coopmat). The q8_0 MMQ matmuls
3646+
// dominate the CLIP vision-encode (~65 %, ~90 GFLOPS/s, run #80). The generic large MMQ
3647+
// tile uses only 8 warps/workgroup (block_size 128); widen to a 32-warp shape (block_size
3648+
// 512, wm=16/wn=32 — the valid 16-wide layout the Intel Xe2 branch uses) to raise GPU
3649+
// occupancy on the dominant matmul path. Falls back automatically (shmem check below) if
3650+
// it doesn't fit. Float MMQ path only (q8_0 goes through mul_mat_q_f16).
3651+
l_warptile_mmq = { 512, 128, 128, 32, subgroup_size_8, 32, 2, tm_m, tn_m, tk_m, subgroup_size_8 };
36433652
}
36443653

36453654
l_wg_denoms = { l_warptile[1], l_warptile[2], 1 };
@@ -5024,7 +5033,11 @@ static void ggml_vk_load_shaders(vk_device& device) {
50245033
}
50255034
ggml_vk_create_pipeline(device, device->pipeline_mul_mat_vec_nc_f16_f32, "mul_mat_vec_nc_f16_f32", mul_mat_vec_nc_f16_f32_len, mul_mat_vec_nc_f16_f32_data, "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_nc_push_constants), {1, 1, 1}, {}, 1);
50265035

5027-
ggml_vk_create_pipeline(device, device->pipeline_norm_f32, "norm_f32", norm_f32_len, norm_f32_data, "main", 2, sizeof(vk_op_push_constants), {1, 1, 1}, {}, 1);
5036+
// norm.comp now uses the binary head (4 bindings: input, weight, bias, output) and
5037+
// spec constants {norepeat, do_multiply, do_add}. Plain norm sets both to 0; the fused
5038+
// NORM+MUL+ADD path sets both to 1. Same bytecode for both, mirroring rms_norm.
5039+
ggml_vk_create_pipeline(device, device->pipeline_norm_f32, "norm_f32", norm_f32_len, norm_f32_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0, 0}, 1, true);
5040+
ggml_vk_create_pipeline(device, device->pipeline_norm_mul_add_f32, "norm_mul_add_f32", norm_f32_len, norm_f32_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1, 1}, 1, true);
50285041
ggml_vk_create_pipeline(device, device->pipeline_group_norm_f32, "group_norm_f32", group_norm_f32_len, group_norm_f32_data, "main", 2, sizeof(vk_op_push_constants), {1, 1, 1}, {}, 1);
50295042

50305043
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_f32, "rms_norm_f32", rms_norm_f32_len, rms_norm_f32_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0}, 1, true);
@@ -10924,7 +10937,8 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const
1092410937
return nullptr;
1092510938
case GGML_OP_NORM:
1092610939
if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
10927-
return ctx->device->pipeline_norm_f32;
10940+
// num_additional_fused_ops == 2 means fused NORM+MUL+ADD (layernorm scale+bias).
10941+
return ctx->num_additional_fused_ops == 2 ? ctx->device->pipeline_norm_mul_add_f32 : ctx->device->pipeline_norm_f32;
1092810942
}
1092910943
return nullptr;
1093010944
case GGML_OP_GROUP_NORM:
@@ -11491,6 +11505,10 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co
1149111505

1149211506
switch (op) {
1149311507
case GGML_OP_NORM:
11508+
// One workgroup per row/channel/sample, matching norm.comp's stride-based
11509+
// indexing (gl_WorkGroupID.{x,y,z} = {row, channel, sample}).
11510+
elements = { (uint32_t)ne01, (uint32_t)ne02, (uint32_t)ne03 };
11511+
break;
1149411512
case GGML_OP_RMS_NORM_BACK:
1149511513
case GGML_OP_L2_NORM:
1149611514
case GGML_OP_SOFT_MAX:
@@ -12550,10 +12568,51 @@ static void ggml_vk_geglu_back(ggml_backend_vk_context * ctx, vk_context& subctx
1255012568
ggml_vk_op_f32<vk_op_push_constants>(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_GEGLU_BACK, { (uint32_t)ggml_nelements(dst), (uint32_t)dst->ne[0], 0.0f, 0.0f, 0.0f, 0.0f });
1255112569
}
1255212570

12553-
static void ggml_vk_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst) {
12554-
float * op_params = (float *)dst->op_params;
12571+
static void ggml_vk_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const struct ggml_cgraph * cgraph, int node_idx) {
12572+
ggml_tensor * norm = cgraph->nodes[node_idx];
12573+
float * op_params = (float *)norm->op_params;
12574+
12575+
ggml_tensor * dst;
12576+
const ggml_tensor * src0; // norm input (A)
12577+
const ggml_tensor * src1; // weight (B)
12578+
const ggml_tensor * src2; // bias (C)
12579+
12580+
if (ctx->num_additional_fused_ops == 2) {
12581+
// fused NORM + MUL + ADD (layernorm scale + bias)
12582+
ggml_tensor * mul = cgraph->nodes[node_idx + 1];
12583+
ggml_tensor * add = cgraph->nodes[node_idx + 2];
12584+
ggml_tensor * weight = mul->src[0] == norm ? mul->src[1] : mul->src[0];
12585+
ggml_tensor * bias = add->src[0] == mul ? add->src[1] : add->src[0];
12586+
// shader uses plain col indexing (no stride), requires zero misalignment
12587+
GGML_ASSERT(get_misalign_bytes(ctx, weight) == 0);
12588+
GGML_ASSERT(get_misalign_bytes(ctx, bias) == 0);
12589+
dst = add;
12590+
src0 = norm->src[0];
12591+
src1 = weight;
12592+
src2 = bias;
12593+
} else {
12594+
dst = norm;
12595+
// plain norm: bind weight/bias to the input so all 4 descriptors are valid.
12596+
// do_multiply/do_add spec constants are false, so they are never read.
12597+
src0 = norm->src[0];
12598+
src1 = norm->src[0];
12599+
src2 = norm->src[0];
12600+
}
12601+
12602+
const uint32_t src0_type_size = ggml_type_size(src0->type);
12603+
const uint32_t src1_type_size = ggml_type_size(src1->type);
12604+
const uint32_t src2_type_size = ggml_type_size(src2->type);
1255512605

12556-
ggml_vk_op_f32<vk_op_push_constants>(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f, 0.0f, 0.0f });
12606+
vk_op_binary_push_constants bin {
12607+
(uint32_t)ggml_nelements(src0),
12608+
(uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2], (uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size,
12609+
(uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2], (uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size,
12610+
(uint32_t)src2->ne[0], (uint32_t)src2->ne[1], (uint32_t)src2->ne[2], (uint32_t)src2->ne[3], (uint32_t)src2->nb[0] / src2_type_size, (uint32_t)src2->nb[1] / src2_type_size, (uint32_t)src2->nb[2] / src2_type_size, (uint32_t)src2->nb[3] / src2_type_size,
12611+
0,
12612+
op_params[0], 0.0f, 0,
12613+
};
12614+
12615+
ggml_vk_op_f32<vk_op_binary_push_constants>(ctx, subctx, src0, src1, src2, nullptr, dst, GGML_OP_NORM, std::move(bin));
1255712616
}
1255812617

1255912618
static void ggml_vk_group_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst) {
@@ -14859,7 +14918,7 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr
1485914918

1486014919
break;
1486114920
case GGML_OP_NORM:
14862-
ggml_vk_norm(ctx, compute_ctx, src0, node);
14921+
ggml_vk_norm(ctx, compute_ctx, cgraph, node_idx);
1486314922

1486414923
break;
1486514924
case GGML_OP_GROUP_NORM:
@@ -15768,6 +15827,50 @@ static bool ggml_vk_can_fuse(const ggml_backend_vk_context * ctx, const struct g
1576815827
return false;
1576915828
}
1577015829
}
15830+
if (ops.size() == 3 && ops.begin()[0] == GGML_OP_NORM && ops.begin()[1] == GGML_OP_MUL && ops.begin()[2] == GGML_OP_ADD) {
15831+
// fused layernorm (NORM) + MUL (scale) + ADD (bias)
15832+
const ggml_tensor *norm = cgraph->nodes[node_idx];
15833+
const ggml_tensor *mul = cgraph->nodes[node_idx + 1];
15834+
const ggml_tensor *add = cgraph->nodes[node_idx + 2];
15835+
15836+
// f32-only
15837+
if (norm->src[0]->type != GGML_TYPE_F32 || norm->type != GGML_TYPE_F32 ||
15838+
mul->type != GGML_TYPE_F32 ||
15839+
add->type != GGML_TYPE_F32) {
15840+
return false;
15841+
}
15842+
// MUL must consume the NORM result as src[0], ADD must consume the MUL result as src[0].
15843+
if (mul->src[0] != norm || add->src[0] != mul) {
15844+
return false;
15845+
}
15846+
const ggml_tensor *weight = mul->src[1];
15847+
const ggml_tensor *bias = add->src[1];
15848+
if (weight->type != GGML_TYPE_F32 || bias->type != GGML_TYPE_F32) {
15849+
return false;
15850+
}
15851+
// weight/bias must be 1-D row vectors broadcast over rows, matching norm->ne[0].
15852+
if (weight->ne[0] != norm->ne[0] || bias->ne[0] != norm->ne[0]) {
15853+
return false;
15854+
}
15855+
if (ggml_nrows(weight) != 1 || ggml_nrows(bias) != 1) {
15856+
return false;
15857+
}
15858+
// mul/add outputs must match norm input shape (no broadcast batch dims)
15859+
if (!ggml_are_same_shape(norm->src[0], add)) {
15860+
return false;
15861+
}
15862+
// contiguous and aligned (shader assumes contiguous rows and zero misalignment)
15863+
if (!ggml_is_contiguous(weight) || !ggml_is_contiguous(bias)) {
15864+
return false;
15865+
}
15866+
if (!ggml_is_contiguous_rows(norm->src[0])) {
15867+
return false;
15868+
}
15869+
if (get_misalign_bytes(ctx, weight) != 0 || get_misalign_bytes(ctx, bias) != 0) {
15870+
return false;
15871+
}
15872+
}
15873+
1577115874
auto const &mm_add_ok = [&](const ggml_tensor *mul, const ggml_tensor *add) {
1577215875
const ggml_tensor *bias = add->src[0] == mul ? add->src[1] : add->src[0];
1577315876

@@ -16324,6 +16427,14 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg
1632416427
// they are overwritten, and one workgroup per row. So close enough.
1632516428
op_srcs_fused_elementwise[0] = true;
1632616429
op_srcs_fused_elementwise[1] = true;
16430+
} else if (ggml_vk_can_fuse(ctx, cgraph, i, { GGML_OP_NORM, GGML_OP_MUL, GGML_OP_ADD })) {
16431+
ctx->num_additional_fused_ops = 2;
16432+
fusion_string = "NORM_MUL_ADD";
16433+
// norm is not elementwise, but whole rows are consumed by one workgroup per
16434+
// row and the mean/variance are computed before output is written. So close enough.
16435+
op_srcs_fused_elementwise[0] = true;
16436+
op_srcs_fused_elementwise[1] = true;
16437+
op_srcs_fused_elementwise[2] = true;
1632716438
} else if (ggml_can_fuse_subgraph(cgraph, i, { GGML_OP_ROPE, GGML_OP_VIEW, GGML_OP_SET_ROWS }, { i + 2 }) &&
1632816439
ggml_check_edges(cgraph, i, rope_view_set_rows_edges) &&
1632916440
ggml_vk_can_fuse_rope_set_rows(ctx, cgraph, i)) {
@@ -16652,6 +16763,10 @@ static void ggml_vk_graph_optimize(ggml_backend_t backend, struct ggml_cgraph *
1665216763
if (!used[c] &&
1665316764
is_src_of(graph->nodes[j], graph->nodes[c]) &&
1665416765
!(j == c+1 && c == current_set.back() && graph->nodes[c]->op == GGML_OP_RMS_NORM && graph->nodes[j]->op == GGML_OP_MUL) &&
16766+
!(j == c+1 && c == current_set.back() && graph->nodes[c]->op == GGML_OP_NORM && graph->nodes[j]->op == GGML_OP_MUL) &&
16767+
// Keep NORM->MUL->ADD consecutive: allow MUL->ADD only when MUL follows a NORM.
16768+
!(j == c+1 && c == current_set.back() && graph->nodes[c]->op == GGML_OP_MUL && graph->nodes[j]->op == GGML_OP_ADD &&
16769+
current_set.size() >= 2 && graph->nodes[current_set[current_set.size() - 2]]->op == GGML_OP_NORM) &&
1665516770
!(j == c+1 && c == current_set.back() && graph->nodes[c]->op == GGML_OP_MUL_MAT && graph->nodes[j]->op == GGML_OP_ADD) &&
1665616771
!(j == c+1 && c == current_set.back() && graph->nodes[c]->op == GGML_OP_MUL_MAT_ID && graph->nodes[j]->op == GGML_OP_ADD_ID) &&
1665716772
!(j == c+1 && c == current_set.back() && graph->nodes[c]->op == GGML_OP_MUL_MAT_ID && graph->nodes[j]->op == GGML_OP_MUL) &&
@@ -17936,11 +18051,24 @@ static ggml_backend_dev_t ggml_backend_vk_reg_get_device(ggml_backend_reg_t reg,
1793618051
return devices[device];
1793718052
}
1793818053

18054+
static bool ggml_backend_vk_supports_efficient_fa(ggml_backend_t backend) {
18055+
ggml_backend_vk_context * ctx = (ggml_backend_vk_context *)backend->context;
18056+
return ctx->device->coopmat2 || ctx->device->coopmat1_fa_support;
18057+
}
18058+
18059+
static void * ggml_backend_vk_reg_get_proc_address(ggml_backend_reg_t reg, const char * name) {
18060+
GGML_UNUSED(reg);
18061+
if (strcmp(name, "ggml_backend_supports_efficient_fa") == 0) {
18062+
return (void *)ggml_backend_vk_supports_efficient_fa;
18063+
}
18064+
return NULL;
18065+
}
18066+
1793918067
static const struct ggml_backend_reg_i ggml_backend_vk_reg_i = {
1794018068
/* .get_name = */ ggml_backend_vk_reg_get_name,
1794118069
/* .get_device_count = */ ggml_backend_vk_reg_get_device_count,
1794218070
/* .get_device = */ ggml_backend_vk_reg_get_device,
17943-
/* .get_proc_address = */ NULL,
18071+
/* .get_proc_address = */ ggml_backend_vk_reg_get_proc_address,
1794418072
};
1794518073

1794618074
ggml_backend_reg_t ggml_backend_vk_reg() {

ggml/src/ggml-vulkan/vulkan-shaders/generic_binary_head.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ layout (push_constant) uniform parameter
1919
#endif
2020
} p;
2121

22-
#if !RMS_NORM_ROPE_FUSION
22+
#if !RMS_NORM_ROPE_FUSION && !NORM_MUL_ADD_FUSION
2323
layout (binding = 0) readonly buffer A {A_TYPE data_a[];};
2424
#if defined(A_TYPE_PACKED16)
2525
layout (binding = 0) readonly buffer A_PACKED16 {A_TYPE_PACKED16 data_a_packed16[];};
Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,84 @@
11
#version 450
22

3-
#include "generic_head.glsl"
3+
#define NORM_MUL_ADD_FUSION 1
4+
5+
#include "generic_binary_head.glsl"
46
#include "types.glsl"
57

68
#extension GL_EXT_control_flow_attributes : enable
79
#define BLOCK_SIZE 512
810

11+
// Spec constant 0 (norepeat) is declared in generic_binary_head.glsl.
12+
layout (constant_id = 1) const bool do_multiply = false;
13+
layout (constant_id = 2) const bool do_add = false;
14+
915
layout(local_size_x = BLOCK_SIZE, local_size_y = 1, local_size_z = 1) in;
1016

11-
layout (binding = 0) readonly buffer X {A_TYPE data_a[];};
12-
layout (binding = 1) writeonly buffer D {D_TYPE data_d[];};
17+
// Bindings: 0=input(A), 1=weight(B), 2=bias(C), 3=output(D).
18+
// When NORM_MUL_ADD_FUSION is set, generic_binary_head.glsl does not declare
19+
// any bindings, so we declare all four here. The weight/bias buffers are only
20+
// read when do_multiply/do_add are true; otherwise they may alias the input.
21+
layout (binding = 0) readonly buffer A {A_TYPE data_a[];};
22+
layout (binding = 1) readonly buffer B {B_TYPE data_b[];};
23+
layout (binding = 2) readonly buffer C {B_TYPE data_c[];};
24+
layout (binding = 3) writeonly buffer D {D_TYPE data_d[];};
1325

14-
shared vec2 sum[BLOCK_SIZE];
26+
shared vec2 sumsh[BLOCK_SIZE];
1527

1628
void main() {
17-
const uint row = gl_WorkGroupID.z * 262144 + gl_WorkGroupID.y * 512 + gl_WorkGroupID.x;
18-
const uint tid = gl_LocalInvocationID.x;
29+
const uint ncols = p.ne00;
30+
const uint nrows = gl_NumWorkGroups.x;
31+
const uint nchannels = gl_NumWorkGroups.y;
32+
33+
const uint row = gl_WorkGroupID.x;
34+
const uint channel = gl_WorkGroupID.y;
35+
const uint samp = gl_WorkGroupID.z;
36+
const uint tid = gl_LocalInvocationID.x;
37+
38+
const uint stride_row = p.nb01;
39+
const uint stride_channel = p.nb02;
40+
const uint stride_sample = p.nb03;
1941

20-
sum[tid] = vec2(0.0f, 0.0f);
42+
// Input offset mirrors rms_norm.comp's stride-based scheme.
43+
uint32_t a_offset = samp*stride_sample + channel*stride_channel + row*stride_row + get_aoffset();
44+
uint32_t b_offset = get_boffset();
45+
// Bias (C) is required to be aligned (misalign==0) by the fusion gate.
46+
uint32_t c_offset = 0;
47+
// Output is contiguous per row (CLIP layernorm dst), matching rms_norm.comp.
48+
uint32_t d_offset = ((samp*nchannels + channel)*nrows + row)*ncols + get_doffset();
2149

22-
[[unroll]] for (uint col = tid; col < p.KX; col += BLOCK_SIZE) {
23-
const float xi = float(data_a[row*p.KX + col]);
24-
sum[tid].x += xi;
25-
sum[tid].y += xi * xi;
50+
vec2 sum = vec2(0.0f, 0.0f);
51+
52+
[[unroll]] for (uint col = tid; col < ncols; col += BLOCK_SIZE) {
53+
const float xi = float(data_a[a_offset + col]);
54+
sum.x += xi;
55+
sum.y += xi * xi;
2656
}
2757

58+
sumsh[tid] = sum;
2859
// sum up partial sums and write back result
2960
barrier();
3061
[[unroll]] for (int s = BLOCK_SIZE / 2; s > 0; s >>= 1) {
3162
if (tid < s) {
32-
sum[tid] += sum[tid + s];
63+
sum += sumsh[tid + s];
64+
sumsh[tid] = sum;
3365
}
3466
barrier();
3567
}
68+
sum = sumsh[0];
3669

37-
const float mean = sum[0].x / p.KX;
38-
const float var = sum[0].y / p.KX - mean * mean;
70+
const float mean = sum.x / float(ncols);
71+
const float var = sum.y / float(ncols) - mean * mean;
3972
const float inv_std = inversesqrt(var + p.param1);
4073

41-
[[unroll]] for (uint col = tid; col < p.KX; col += BLOCK_SIZE) {
42-
data_d[row*p.KX + col] = D_TYPE((float(data_a[row*p.KX + col]) - mean) * inv_std);
74+
[[unroll]] for (uint col = tid; col < ncols; col += BLOCK_SIZE) {
75+
float result = (float(data_a[a_offset + col]) - mean) * inv_std;
76+
if (do_multiply) {
77+
result *= float(data_b[b_offset + col]);
78+
}
79+
if (do_add) {
80+
result += float(data_c[c_offset + col]);
81+
}
82+
data_d[d_offset + col] = D_TYPE(result);
4383
}
4484
}

ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,7 @@ void process_shaders() {
994994
string_to_spv("mul_mat_vec_nc_f16_f32", "mul_mat_vec_nc.comp", {{"A_TYPE", "float16_t"}, {"A_TYPEV4", "f16vec4"}, {"B_TYPE", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}});
995995

996996
// Norms
997-
string_to_spv("norm_f32", "norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
997+
string_to_spv("norm_f32", "norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
998998
string_to_spv("group_norm_f32", "group_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
999999
string_to_spv("rms_norm_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
10001000
string_to_spv("rms_norm_partials_f32", "rms_norm_partials.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));

0 commit comments

Comments
 (0)