@@ -10,6 +10,7 @@ constant bool do_causal [[function_constant(22)]];
1010constant bool bool_mask [[function_constant(23)]] ;
1111constant bool float_mask [[function_constant(24)]] ;
1212constant bool has_sinks [[function_constant(25)]] ;
13+ constant int blocks [[function_constant(26)]] ;
1314
1415template <typename T, int D, int V = D>
1516[[kernel]] void sdpa_vector (
@@ -180,10 +181,9 @@ template <typename T, int D, int V = D>
180181 const device T* queries [[buffer(0)]] ,
181182 const device T* keys [[buffer(1)]] ,
182183 const device T* values [[buffer(2)]] ,
183- device float * out [[buffer(3)]] ,
184+ device T * out [[buffer(3)]] ,
184185 device float * sums [[buffer(4)]] ,
185186 device float * maxs [[buffer(5)]] ,
186- const constant int & gqa_factor [[buffer(6)]] ,
187187 const constant int & N [[buffer(7)]] ,
188188 const constant size_t & k_head_stride [[buffer(8)]] ,
189189 const constant size_t & k_seq_stride [[buffer(9)]] ,
@@ -199,94 +199,81 @@ template <typename T, int D, int V = D>
199199 const constant int & mask_head_stride
200200 [[buffer(17), function_constant(has_mask)]] ,
201201 const device T* sinks [[buffer(18), function_constant(has_sinks)]] ,
202- const constant int & num_q_heads
203- [[buffer(19), function_constant(has_sinks) ]] ,
202+ uint3 tptg [[threads_per_threadgroup]] ,
203+ uint3 tidtg [[thread_position_in_threadgroup ]] ,
204204 uint3 tid [[threadgroup_position_in_grid]] ,
205205 uint3 tpg [[threadgroups_per_grid]] ,
206- uint simd_gid [[simdgroup_index_in_threadgroup]] ,
207206 uint simd_lid [[thread_index_in_simdgroup]] ) {
208- constexpr int BN = 8 ;
209207 constexpr int BD = 32 ;
210208 constexpr int qk_per_thread = D / BD ;
211209 constexpr int v_per_thread = V / BD ;
212- int inner_k_stride = BN * int (k_seq_stride);
213- int inner_v_stride = BN * int (v_seq_stride);
214- constexpr int blocks = 32 ;
215210
216211 typedef float U;
217212
218213 thread U q[qk_per_thread];
219- thread U k[qk_per_thread];
220- thread U o[v_per_thread];
221-
222- threadgroup U outputs[BN * BD ];
223- threadgroup U max_scores[BN ];
224- threadgroup U sum_exp_scores[BN ];
214+ thread U o[v_per_thread] = {0 };
225215
226216 // Adjust positions
217+ const int kv_head_idx = tid.x ;
218+ const int batch_idx = tid.y ;
227219 const int block_idx = tid.z ;
228- const int q_batch_head_idx = tid.x ;
229- const int q_seq_idx = tid.y ;
230- const int o_offset = q_batch_head_idx * tpg.y + q_seq_idx;
220+ const int gqa_factor = tptg.y ;
221+ const int q_seq_len = tptg.z ;
222+ const int q_seq_idx = tidtg.z ;
223+ const int q_head_idx = gqa_factor * kv_head_idx + tidtg.y ;
224+ const int num_kv_heads = tpg.x ;
225+ const int num_q_heads = num_kv_heads * gqa_factor;
226+ const int q_batch_head_idx = (batch_idx * num_q_heads + q_head_idx);
227+ const int o_offset = q_batch_head_idx * q_seq_len + q_seq_idx;
231228 const int q_offset =
232- query_transposed ? tpg.x * q_seq_idx + q_batch_head_idx : o_offset;
233- const int kv_head_idx = q_batch_head_idx / gqa_factor;
229+ query_transposed ? num_q_heads * q_seq_idx + q_batch_head_idx : o_offset;
234230
235231 queries += q_offset * D + simd_lid * qk_per_thread;
236- keys += kv_head_idx * k_head_stride +
237- (block_idx * BN + simd_gid) * k_seq_stride + simd_lid * qk_per_thread;
238- values += kv_head_idx * v_head_stride +
239- (block_idx * BN + simd_gid) * v_seq_stride + simd_lid * v_per_thread;
232+
233+ const int kv_batch_head_idx = batch_idx * num_kv_heads + kv_head_idx;
234+ keys += kv_batch_head_idx * k_head_stride + block_idx * k_seq_stride +
235+ simd_lid * qk_per_thread;
236+ values += kv_batch_head_idx * v_head_stride + block_idx * v_seq_stride +
237+ simd_lid * v_per_thread;
240238 out += o_offset * blocks * V + block_idx * V + simd_lid * v_per_thread;
241239 if (bool_mask) {
242240 bmask += q_batch_head_idx * mask_head_stride +
243- (block_idx * BN + simd_gid) * mask_kv_seq_stride +
244- q_seq_idx * mask_q_seq_stride;
241+ block_idx * mask_kv_seq_stride + q_seq_idx * mask_q_seq_stride;
245242 }
246243 if (float_mask) {
247244 fmask += q_batch_head_idx * mask_head_stride +
248- (block_idx * BN + simd_gid) * mask_kv_seq_stride +
249- q_seq_idx * mask_q_seq_stride;
245+ block_idx * mask_kv_seq_stride + q_seq_idx * mask_q_seq_stride;
250246 }
251247 sums += o_offset * blocks + block_idx;
252248 maxs += o_offset * blocks + block_idx;
253249
254- // Read the query and 0 the output accumulator
250+ // Read the query
255251 for (int i = 0 ; i < qk_per_thread; i++) {
256252 q[i] = static_cast <U>(scale) * queries[i];
257253 }
258- for (int i = 0 ; i < v_per_thread; i++) {
259- o[i] = 0 ;
260- }
261254
262255 U max_score = Limits<U>::finite_min;
263256 U sum_exp_score = 0 ;
264- if (has_sinks && block_idx == 0 && simd_gid == 0 ) {
265- int q_head_idx = q_batch_head_idx % num_q_heads;
257+ if (has_sinks && block_idx == 0 ) {
266258 max_score = static_cast <U>(sinks[q_head_idx]);
267259 sum_exp_score = 1 ;
268260 }
269261
270262 // For each key
271- for (int i = block_idx * BN + simd_gid ; i < N; i += blocks * BN ) {
263+ for (int i = block_idx; i < N; i += blocks) {
272264 bool use_key = true ;
273265 if (do_causal) {
274- use_key = i <= (N - int (tpg. y ) + int (q_seq_idx));
266+ use_key = i <= (N - q_seq_len + int (q_seq_idx));
275267 } else if (bool_mask) {
276268 use_key = bmask[0 ];
277269 } else if (float_mask) {
278270 use_key = (fmask[0 ] >= Limits<T>::finite_min);
279271 }
280272 if (use_key) {
281- // Read the key
282- for (int i = 0 ; i < qk_per_thread; i++) {
283- k[i] = keys[i];
284- }
285-
286273 // Compute the i-th score
287274 U score = 0 ;
288275 for (int i = 0 ; i < qk_per_thread; i++) {
289- score += q[i] * k [i];
276+ score += q[i] * keys [i];
290277 }
291278 score = simd_sum (score);
292279
@@ -309,57 +296,30 @@ template <typename T, int D, int V = D>
309296 }
310297
311298 // Move the pointers to the next kv
312- keys += blocks * inner_k_stride ;
313- values += blocks * inner_v_stride ;
299+ keys += blocks * int (k_seq_stride) ;
300+ values += blocks * int (v_seq_stride) ;
314301 if (bool_mask) {
315- bmask += BN * blocks * mask_kv_seq_stride;
302+ bmask += blocks * mask_kv_seq_stride;
316303 }
317304 if (float_mask) {
318- fmask += BN * blocks * mask_kv_seq_stride;
305+ fmask += blocks * mask_kv_seq_stride;
319306 }
320307 }
321308
322- // Each thread has a partial part of the output so we need to combine them.
323-
324- // First let's communicate the max and sum_exp
309+ // Write the sum and max and outputs
325310 if (simd_lid == 0 ) {
326- max_scores[simd_gid] = max_score;
327- sum_exp_scores[simd_gid] = sum_exp_score;
328- }
329- threadgroup_barrier (mem_flags::mem_threadgroup);
330- max_score = (simd_lid < BN ) ? max_scores[simd_lid] : -1e9 ;
331- U new_max = simd_max (max_score);
332- U factor = fast::exp (max_score - new_max);
333- sum_exp_score = (simd_lid < BN ) ? sum_exp_scores[simd_lid] : 0 ;
334- sum_exp_score = simd_sum (sum_exp_score * factor);
335-
336- // Write the sum and new max
337- if (simd_gid == 0 ) {
338311 sums[0 ] = sum_exp_score;
339- maxs[0 ] = new_max ;
312+ maxs[0 ] = max_score ;
340313 }
341314
342- // Now we need to aggregate all the outputs
343315 for (int i = 0 ; i < v_per_thread; i++) {
344- outputs[simd_lid * BN + simd_gid] =
345- o[i] * fast::exp (max_scores[simd_gid] - new_max);
346- threadgroup_barrier (mem_flags::mem_threadgroup);
347-
348- // And write the output
349- if (simd_gid == 0 ) {
350- U output = outputs[simd_lid * BN ];
351- for (int j = 1 ; j < BN ; j++) {
352- output += outputs[simd_lid * BN + j];
353- }
354- out[i] = static_cast <T>(output);
355- }
356- threadgroup_barrier (mem_flags::mem_threadgroup);
316+ out[i] = static_cast <T>(o[i]);
357317 }
358318}
359319
360320template <typename T, int D>
361321[[kernel]] void sdpa_vector_2pass_2 (
362- const device float * partials [[buffer(0)]] ,
322+ const device T * partials [[buffer(0)]] ,
363323 const device float * sums [[buffer(1)]] ,
364324 const device float * maxs [[buffer(2)]] ,
365325 device T* out [[buffer(3)]] ,
@@ -370,38 +330,56 @@ template <typename T, int D>
370330 constexpr int BN = 32 ;
371331 constexpr int BD = 32 ;
372332 constexpr int elem_per_thread = D / BD ;
373- constexpr int blocks = 32 ;
374333
375334 typedef float U;
376335
377- thread U o[elem_per_thread];
336+ thread U o[elem_per_thread] = { 0 } ;
378337 threadgroup U outputs[BN * BD ];
379338
380339 // Adjust positions
381340 const int head_idx = tid.x ;
382341 const int q_seq_idx = tid.y ;
383342 const int q_offset = head_idx * tpg.y + q_seq_idx;
384- ;
385343 partials += q_offset * blocks * D + simd_gid * D + simd_lid * elem_per_thread;
386344 sums += q_offset * blocks;
387345 maxs += q_offset * blocks;
388346 out += q_offset * D + simd_gid * elem_per_thread;
389347
390- // First everybody reads the max and sum_exp
391- U max_score = maxs[simd_lid];
392- U new_max = simd_max (max_score);
393- U factor = fast::exp (max_score - new_max);
394- U sum_exp_score = simd_sum (sums[simd_lid] * factor);
348+ // Set defaults
349+ U sum_exp_score = 0.0 ;
350+ U max_score = Limits<U>::finite_min;
395351
396- // Now read the block into registers and then use shared memory to transpose
397- // it
398- for (int i = 0 ; i < elem_per_thread; i++) {
399- o[i] = partials[i];
352+ // Reduce the max
353+ for (int b = 0 ; b < blocks / BN ; ++b) {
354+ max_score = max (max_score, maxs[simd_lid + BN * b]);
355+ }
356+ max_score = simd_max (max_score);
357+
358+ // Reduce the d
359+ for (int b = 0 ; b < blocks / BN ; ++b) {
360+ U factor = fast::exp (maxs[simd_lid + BN * b] - max_score);
361+ sum_exp_score += factor * sums[simd_lid + BN * b];
400362 }
363+ sum_exp_score = simd_sum (sum_exp_score);
364+
365+ // Reduce the sum exp and partials
366+ for (int b = 0 ; b < blocks / BN ; ++b) {
367+ U factor = fast::exp (maxs[simd_gid] - max_score);
368+
369+ // Update the output accumulator
370+ for (int i = 0 ; i < elem_per_thread; i++) {
371+ o[i] += factor * partials[i];
372+ }
373+ maxs += BN ;
374+ sums += BN ;
375+ partials += BN * D;
376+ }
377+
378+ // Use shared memory to transpose and reduce the final block
401379 for (int i = 0 ; i < elem_per_thread; i++) {
402380 outputs[simd_lid * BD + simd_gid] = o[i];
403381 threadgroup_barrier (mem_flags::mem_threadgroup);
404- o[i] = simd_sum (outputs[simd_gid * BD + simd_lid] * factor );
382+ o[i] = simd_sum (outputs[simd_gid * BD + simd_lid]);
405383 o[i] = sum_exp_score == 0 ? o[i] : (o[i] / sum_exp_score);
406384 threadgroup_barrier (mem_flags::mem_threadgroup);
407385 }
0 commit comments