char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Jeff Bolzvulkan: extend topk_moe fusion to support sqrt(softplus) (#26124)a7a6d0d26

char/aliceai-t5-moe
7.1 KiB221 linesraw
1#version 450
2
3#extension GL_EXT_control_flow_attributes : require
4#extension GL_KHR_shader_subgroup_basic : enable
5#extension GL_KHR_shader_subgroup_arithmetic : enable
6#extension GL_KHR_shader_subgroup_shuffle : enable
7
8#include "types.glsl"
9
10#define GATING_FUNC_SOFTMAX 0
11#define GATING_FUNC_SIGMOID 1
12#define GATING_FUNC_SOFTMAX_WEIGHT 2
13#define GATING_FUNC_SQRT_SOFTPLUS 3
14
15layout (push_constant) uniform parameter
16{
17    uint n_rows;
18    uint n_experts_push;
19    uint n_expert_used;
20    float clamp_min;
21    float clamp_max;
22    uint gating_func;
23    uint has_bias;
24    uint with_norm;
25    float output_scale;
26    float output_bias;
27};
28
29layout(local_size_x_id = 0, local_size_y = 4, local_size_z = 1) in;
30
31layout(constant_id = 0) const uint WARP_SIZE = 32;
32layout(constant_id = 1) const uint n_experts_spec = 512;
33layout(constant_id = 2) const bool nexperts_use_push = false;
34
35uint n_experts = nexperts_use_push ? n_experts_push : n_experts_spec;
36
37#define CEIL_DIV(a, b) (((a) + (b) - 1) / (b))
38
39const uint experts_per_thread = CEIL_DIV(n_experts_spec, WARP_SIZE);
40
41layout (binding = 0, std430) readonly buffer Logits {float logits[];};
42layout (binding = 1, std430) readonly buffer BiasProbs {float bias[];};
43layout (binding = 2, std430) writeonly buffer Weights {float weights[];};
44layout (binding = 3, std430) writeonly buffer Ids {uint ids[];};
45
46const float INFINITY = 1.0 / 0.0;
47
48// Warp-local softmax used for both the pre-top-k logits and the post-top-k delayed path.
49void softmax_warp_inplace(inout float vals[experts_per_thread], const uint limit, const uint lane, const bool use_limit) {
50    float max_val = -INFINITY;
51
52    [[unroll]]
53    for (int i = 0; i < experts_per_thread; i++) {
54        const uint idx       = lane + i * WARP_SIZE;
55        const bool is_active = !use_limit || (idx < limit);
56        if (is_active) {
57            max_val = max(max_val, vals[i]);
58        }
59    }
60
61    max_val = subgroupMax(max_val);
62
63    float sum = 0.f;
64
65    [[unroll]]
66    for (int i = 0; i < experts_per_thread; i++) {
67        const uint idx       = lane + i * WARP_SIZE;
68        const bool is_active = !use_limit || (idx < limit);
69        if (is_active) {
70            const float val = exp(vals[i] - max_val);
71            vals[i]         = val;
72            sum += val;
73        } else {
74            vals[i] = 0.f;
75        }
76    }
77
78    sum = subgroupAdd(sum);
79
80    const float inv_sum = 1.0f / sum;
81
82    [[unroll]]
83    for (int i = 0; i < experts_per_thread; i++) {
84        const uint idx       = lane + i * WARP_SIZE;
85        const bool is_active = !use_limit || (idx < limit);
86        if (is_active) {
87            vals[i] *= inv_sum;
88        }
89    }
90}
91
92void main() {
93    const uint row = gl_WorkGroupID.x * gl_WorkGroupSize.y + gl_SubgroupID;
94    if (row >= n_rows) {
95        return;
96    }
97
98    const uint logits_offset = n_experts * row;
99    const uint bias_offset = 0; // 1D
100    const uint weights_offset = n_expert_used * row;
101    const uint ids_offset = n_experts * row;
102    const uint lane = gl_SubgroupInvocationID;
103
104    float probs[experts_per_thread];
105    [[unroll]]
106    for (int i = 0; i < experts_per_thread; i++) {
107        probs[i] = -INFINITY;
108    }
109
110    [[unroll]]
111    for (uint i = 0; i < n_experts; i += WARP_SIZE) {
112        const uint expert = i + lane;
113        probs[i / WARP_SIZE] = (n_experts % WARP_SIZE == 0 || expert < n_experts) ? logits[logits_offset + expert] : -INFINITY;
114    }
115
116    if (gating_func == GATING_FUNC_SOFTMAX) {
117        softmax_warp_inplace(probs, n_experts, lane, nexperts_use_push);
118    } else if (gating_func == GATING_FUNC_SIGMOID) {
119        [[unroll]]
120        for (uint i = 0; i < n_experts; i += WARP_SIZE) {
121            const uint expert = i + lane;
122            probs[i / WARP_SIZE] = (n_experts % WARP_SIZE == 0 || expert < n_experts) ? 1.f / (1.f + exp(-probs[i / WARP_SIZE])) : -INFINITY;
123        }
124    } else if (gating_func == GATING_FUNC_SQRT_SOFTPLUS) {
125        [[unroll]]
126        for (uint i = 0; i < n_experts; i += WARP_SIZE) {
127            const uint expert = i + lane;
128            const float val = probs[i / WARP_SIZE];
129            probs[i / WARP_SIZE] = (n_experts % WARP_SIZE == 0 || expert < n_experts) ? sqrt(val > 20.0f ? val : log(1.0f + exp(val))) : -INFINITY;
130        }
131    }
132
133    float selection_probs[experts_per_thread];
134    if (has_bias != 0) {
135        [[unroll]]
136        for (uint i = 0; i < n_experts; i += WARP_SIZE) {
137            const uint expert = i + lane;
138            selection_probs[i / WARP_SIZE] = (n_experts % WARP_SIZE == 0 || expert < n_experts) ? probs[i / WARP_SIZE] + bias[bias_offset + expert] : -INFINITY;
139        }
140    } else {
141        [[unroll]]
142        for (int i = 0; i < experts_per_thread; i++) {
143            selection_probs[i] = probs[i];
144        }
145    }
146
147    // at this point, each thread holds a portion of softmax,
148    // we do the argmax reduce over n_expert_used, each time marking
149    // the expert weight as -inf to exclude from the next iteration
150
151    float wt_sum = 0.f;
152
153    float output_weights[experts_per_thread];
154
155    [[unroll]]
156    for (int i = 0; i < experts_per_thread; i++) {
157        output_weights[i] = 0.f;
158    }
159
160    for (int k = 0; k < n_expert_used; k++) {
161        float max_val    = probs[0];
162        float max_val_s  = selection_probs[0];
163        uint   max_expert = lane;
164
165        [[unroll]]
166        for (uint i = WARP_SIZE; i < n_experts; i += WARP_SIZE) {
167            const uint expert = i + lane;
168            if ((n_experts % WARP_SIZE == 0 || expert < n_experts) && selection_probs[i / WARP_SIZE] > max_val_s) {
169                max_val    = probs[i / WARP_SIZE];
170                max_val_s  = selection_probs[i / WARP_SIZE];
171                max_expert = expert;
172            }
173        }
174
175        [[unroll]]
176        for (uint mask = WARP_SIZE / 2; mask > 0; mask /= 2) {
177            const float val    = subgroupShuffleXor(max_val, mask);
178            const float val_s  = subgroupShuffleXor(max_val_s, mask);
179            const uint  expert = subgroupShuffleXor(max_expert, mask);
180            if (val_s > max_val_s || (val_s == max_val_s && expert < max_expert)) {
181                max_val    = val;
182                max_val_s  = val_s;
183                max_expert = expert;
184            }
185        }
186
187        if ((k & (WARP_SIZE - 1)) == lane) {
188            output_weights[k / WARP_SIZE] = max_val;
189        }
190
191        if ((max_expert & (WARP_SIZE - 1)) == lane) {
192            selection_probs[max_expert / WARP_SIZE] = -INFINITY;
193
194            ids[ids_offset + k] = max_expert;
195            wt_sum += max_val;
196        }
197    }
198
199    if (with_norm != 0) {
200        wt_sum              = subgroupAdd(wt_sum);
201        wt_sum              = clamp(wt_sum, clamp_min, clamp_max);
202        const float inv_sum = 1.0f / wt_sum;
203
204        [[unroll]]
205        for (uint i = 0; i < experts_per_thread; ++i) {
206            output_weights[i] *= inv_sum;
207        }
208    }
209
210    if (gating_func == GATING_FUNC_SOFTMAX_WEIGHT) {
211        softmax_warp_inplace(output_weights, n_expert_used, lane, true);
212    }
213
214    [[unroll]]
215    for (uint i = 0; i < experts_per_thread; ++i) {
216        uint idx = i * WARP_SIZE + lane;
217        if (idx < n_expert_used) {
218            weights[weights_offset + idx] = output_scale * output_weights[i] + output_bias;
219        }
220    }
221}