33
44import triton
55import triton .language as tl
6+ from triton .language .extra import libdevice
67
78
89@triton .autotune (
@@ -118,6 +119,7 @@ def _nearest_kernel(
118119 right = M
119120
120121 x_sq = tl .zeros ((BLOCK_N ,), dtype = tl .float32 ) # ||x||^2.
122+ x_sq_c = tl .zeros ((BLOCK_N ,), dtype = tl .float32 )
121123 x_block_ptr_sq = tl .make_block_ptr (
122124 base = x_ptr ,
123125 shape = (D , N ),
@@ -135,7 +137,15 @@ def _nearest_kernel(
135137 boundary_check = (0 , 1 ),
136138 padding_option = "zero" ,
137139 )
138- x_sq += tl .sum (x * x , axis = 0 )
140+ x_term = tl .sum (x * x , axis = 0 )
141+ x_t = x_sq + x_term
142+ x_cond = tl .abs (x_sq ) >= tl .abs (x_term )
143+ x_sq_c += tl .where (
144+ x_cond ,
145+ (x_sq - x_t ) + x_term ,
146+ (x_term - x_t ) + x_sq ,
147+ )
148+ x_sq = x_t
139149 x_block_ptr_sq = tl .advance (x_block_ptr_sq , (BLOCK_K , 0 ))
140150 if COSINE :
141151 inv_x = tl .rsqrt (x_sq + EPS ) # 1/||x||.
@@ -155,7 +165,9 @@ def _nearest_kernel(
155165 full_y = y_start + BLOCK_M <= M # Full tile.
156166
157167 acc = tl .zeros ((BLOCK_M , BLOCK_N ), dtype = tl .float32 ) # Dot acc.
168+ acc_c = tl .zeros ((BLOCK_M , BLOCK_N ), dtype = tl .float32 )
158169 y_sq = tl .zeros ((BLOCK_M ,), dtype = tl .float32 ) # ||y||^2.
170+ y_sq_c = tl .zeros ((BLOCK_M ,), dtype = tl .float32 )
159171
160172 x_block_ptr = x_block_ptr_base
161173 y_block_ptr = tl .make_block_ptr (
@@ -187,16 +199,36 @@ def _nearest_kernel(
187199 boundary_check = (0 , 1 ),
188200 padding_option = "zero" ,
189201 )
190- acc += tl .dot (y , x , input_precision = INPUT_PRECISION )
191- y_sq += tl .sum (y * y , axis = 1 )
202+ dot_term = tl .dot (y , x , input_precision = INPUT_PRECISION )
203+ dot_t = acc + dot_term
204+ dot_cond = tl .abs (acc ) >= tl .abs (dot_term )
205+ acc_c += tl .where (
206+ dot_cond ,
207+ (acc - dot_t ) + dot_term ,
208+ (dot_term - dot_t ) + acc ,
209+ )
210+ acc = dot_t
211+ y_term = tl .sum (y * y , axis = 1 )
212+ y_t = y_sq + y_term
213+ y_cond = tl .abs (y_sq ) >= tl .abs (y_term )
214+ y_sq_c += tl .where (
215+ y_cond ,
216+ (y_sq - y_t ) + y_term ,
217+ (y_term - y_t ) + y_sq ,
218+ )
219+ y_sq = y_t
192220 y_block_ptr = tl .advance (y_block_ptr , (0 , BLOCK_K ))
193221 x_block_ptr = tl .advance (x_block_ptr , (BLOCK_K , 0 ))
194222
195223 if COSINE :
196- inv_y = tl .rsqrt (y_sq + EPS ) # 1/||y||.
224+ inv_y = tl .rsqrt (y_sq + y_sq_c + EPS ) # 1/||y||.
197225 dist = 1.0 - acc * (inv_y [:, None ] * inv_x [None , :])
198226 else :
199- dist = y_sq [:, None ] + x_sq [None , :] - 2.0 * acc
227+ dist = (
228+ (y_sq + y_sq_c )[:, None ]
229+ + (x_sq + x_sq_c )[None , :]
230+ - 2.0 * acc
231+ )
200232
201233 if full_y :
202234 valid = tl .broadcast_to (mask_x [None , :], (BLOCK_M , BLOCK_N ))
@@ -279,7 +311,12 @@ def _nearest_kernel(
279311 args ['BLOCK_N' ],
280312 ),
281313 'EVEN_D' : lambda args : args ['D' ] % args ['BLOCK_D' ] == 0 ,
282- 'K_PAD' : lambda args : triton .next_power_of_2 (args ['K' ])
314+ 'N_BLOCKS_PER_K' : (
315+ lambda args : max (
316+ 1 ,
317+ triton .next_power_of_2 (args ['K' ]) // args ['BLOCK_N' ],
318+ )
319+ ),
283320})
284321@triton .jit
285322def _knn_segmented_kernel (
@@ -297,7 +334,6 @@ def _knn_segmented_kernel(
297334 stride_ym ,
298335 stride_yd ,
299336 K : tl .constexpr ,
300- K_PAD : tl .constexpr ,
301337 USE_BATCH : tl .constexpr ,
302338 INPUT_PRECISION : tl .constexpr ,
303339 BLOCK_N : tl .constexpr ,
@@ -308,20 +344,34 @@ def _knn_segmented_kernel(
308344 COSINE : tl .constexpr ,
309345 EPS : tl .constexpr ,
310346 IGNORE_SAME_INDEX : tl .constexpr ,
347+ N_BLOCKS_PER_K : tl .constexpr ,
311348):
312349 pid = tl .program_id (0 ) # Program id over y rows.
313350 n_y = pid # Current y index.
314351 mask_y = n_y < M # Mask for valid y.
315352
316- k_offsets = tl .arange (0 , K_PAD ) # Offsets for padded top-k buffer.
353+ k_offsets = tl .arange (
354+ 0 ,
355+ N_BLOCKS_PER_K * BLOCK_N ,
356+ ) # Offsets for padded top-k buffer.
317357 INF_KEY = 0x7f800000ffffffff
318358
319359 best_dist_key = tl .full (
320- (K_PAD ,),
360+ (N_BLOCKS_PER_K * BLOCK_N ,),
321361 INF_KEY ,
322362 tl .int64 ,
323363 )
324364
365+ if N_BLOCKS_PER_K > 1 :
366+ acc_key = tl .full (
367+ (N_BLOCKS_PER_K , BLOCK_N ),
368+ INF_KEY ,
369+ tl .int64 ,
370+ )
371+ k_rows = tl .arange (0 , N_BLOCKS_PER_K )[:, None ].broadcast_to (
372+ (N_BLOCKS_PER_K , BLOCK_N )
373+ )
374+
325375 if USE_BATCH :
326376 example_idx = tl .load (
327377 batch_y_ptr + n_y ,
@@ -342,6 +392,7 @@ def _knn_segmented_kernel(
342392 ) # Segment end.
343393 if COSINE :
344394 y_sq = tl .zeros ((1 ,), tl .float32 ) # ||y||^2 accumulator.
395+ y_sq_c = tl .zeros ((1 ,), tl .float32 )
345396 y_norm_ptr = tl .make_block_ptr (
346397 base = y_ptr ,
347398 shape = (D , M ),
@@ -359,7 +410,15 @@ def _knn_segmented_kernel(
359410 boundary_check = (0 , 1 ),
360411 padding_option = "zero" ,
361412 ) # Tail D.
362- y_sq += tl .sum (y * y , axis = 0 )
413+ y_term = tl .sum (y * y , axis = 0 )
414+ y_t = y_sq + y_term
415+ y_cond = tl .abs (y_sq ) >= tl .abs (y_term )
416+ y_sq_c += tl .where (
417+ y_cond ,
418+ (y_sq - y_t ) + y_term ,
419+ (y_term - y_t ) + y_sq ,
420+ )
421+ y_sq = y_t
363422 y_norm_ptr = tl .advance (y_norm_ptr , (BLOCK_D , 0 ))
364423 y_rnorm = tl .rsqrt (y_sq + EPS ) # 1/||y|| for cosine.
365424
@@ -384,9 +443,12 @@ def _knn_segmented_kernel(
384443
385444 if COSINE :
386445 acc_dot = tl .zeros ((BLOCK_N ,), tl .float32 ) # Dot accumulator.
446+ acc_dot_c = tl .zeros ((BLOCK_N ,), tl .float32 )
387447 acc_x_sq = tl .zeros ((BLOCK_N ,), tl .float32 ) # ||x||^2 accumulator.
448+ acc_x_sq_c = tl .zeros ((BLOCK_N ,), tl .float32 )
388449 else :
389450 acc_dist = tl .zeros ((BLOCK_N ,), tl .float32 )
451+ acc_dist_c = tl .zeros ((BLOCK_N ,), tl .float32 )
390452 x_block_start_i32 = x_block_start .to (
391453 tl .int32
392454 ) # Block ptr needs int32 offsets.
@@ -439,19 +501,43 @@ def _knn_segmented_kernel(
439501 y ,
440502 input_precision = INPUT_PRECISION ,
441503 ) # MxV dot.
442- acc_dot += tl .sum (prod , axis = 1 )
443- acc_x_sq += tl .sum (x * x , axis = 1 )
504+ dot_term = tl .sum (prod , axis = 1 )
505+ dot_t = acc_dot + dot_term
506+ dot_cond = tl .abs (acc_dot ) >= tl .abs (dot_term )
507+ acc_dot_c += tl .where (
508+ dot_cond ,
509+ (acc_dot - dot_t ) + dot_term ,
510+ (dot_term - dot_t ) + acc_dot ,
511+ )
512+ acc_dot = dot_t
513+ x_term = tl .sum (x * x , axis = 1 )
514+ x_t = acc_x_sq + x_term
515+ x_cond = tl .abs (acc_x_sq ) >= tl .abs (x_term )
516+ acc_x_sq_c += tl .where (
517+ x_cond ,
518+ (acc_x_sq - x_t ) + x_term ,
519+ (x_term - x_t ) + acc_x_sq ,
520+ )
521+ acc_x_sq = x_t
444522 y_block_ptr = tl .advance (y_block_ptr , (BLOCK_D , 0 ))
445523 else :
446524 diff = x - y
447- acc_dist += tl .sum (diff * diff , axis = 1 )
525+ term = tl .sum (diff * diff , axis = 1 )
526+ t_k = acc_dist + term
527+ k_cond = tl .abs (acc_dist ) >= tl .abs (term )
528+ acc_dist_c += tl .where (
529+ k_cond ,
530+ (acc_dist - t_k ) + term ,
531+ (term - t_k ) + acc_dist ,
532+ )
533+ acc_dist = t_k
448534 y_block_ptr = tl .advance (y_block_ptr , (0 , BLOCK_D ))
449535 x_block_ptr = tl .advance (x_block_ptr , (0 , BLOCK_D ))
450536 if COSINE :
451- x_rnorm = tl .rsqrt (acc_x_sq + EPS ) # 1/||x||.
537+ x_rnorm = tl .rsqrt (acc_x_sq + acc_x_sq_c + EPS ) # 1/||x||.
452538 dist = 1.0 - acc_dot * (x_rnorm * y_rnorm ) # Cosine distance.
453539 else :
454- dist = acc_dist # L2^2 distance.
540+ dist = acc_dist + acc_dist_c # L2^2 distance.
455541
456542 if not full_block :
457543 dist = tl .where (mask_x , dist , float ("inf" )) # Mask invalid x.
@@ -460,30 +546,45 @@ def _knn_segmented_kernel(
460546 same_idx = x_idx == n_y
461547 if not full_block :
462548 same_idx = same_idx & mask_x
463- dist = tl .where (same_idx , float ("inf" ), dist )
464549
465550 dist_bits = dist .to (tl .int32 , bitcast = True )
466551 dist_bits = dist_bits .to (tl .int64 ) # Pack dist.
467552 idx_bits = x_idx .to (tl .int32 ).to (tl .int64 ) & 0xFFFFFFFF # Pack idx.
468553 key = (dist_bits << 32 ) | idx_bits # Lexicographic key.
469- sorted_key = tl .sort (key , descending = False ) # Full block sort.
470- key_k = tl .gather (sorted_key , k_offsets , axis = 0 ) # Take first k keys.
471-
472- combo_key = tl .cat (
473- best_dist_key ,
474- key_k ,
475- can_reorder = True
476- ) # Merge buffers.
477- sorted_key = tl .sort (combo_key , descending = False ) # Sort 2K keys.
478- best_dist_key = tl .gather (
479- sorted_key ,
480- k_offsets ,
481- axis = 0 ,
482- ) # Keep best K.
554+ key = tl .where (libdevice .isinf (dist ), INF_KEY , key )
555+
556+ absorb = False
557+ if N_BLOCKS_PER_K > 1 :
558+ row = xb % N_BLOCKS_PER_K
559+ acc_key = tl .where (k_rows == row , key [None , :], acc_key )
560+ if row == (N_BLOCKS_PER_K - 1 ) or xb == (MAX_X_BLOCKS - 1 ):
561+ absorb = True
562+ final_key = acc_key .ravel ()
563+ acc_key = tl .full (
564+ (N_BLOCKS_PER_K , BLOCK_N ),
565+ INF_KEY ,
566+ tl .int64 ,
567+ )
568+ else :
569+ absorb = True
570+ final_key = key
571+
572+ if absorb :
573+ combo_key = tl .cat (
574+ best_dist_key ,
575+ final_key ,
576+ can_reorder = True
577+ ) # Merge buffers.
578+ sorted_key = tl .sort (combo_key , descending = False ) # Sort 2K keys.
579+ best_dist_key = tl .gather (
580+ sorted_key ,
581+ k_offsets ,
582+ axis = 0 ,
583+ ) # Keep best K.
483584
484585 k_mask = k_offsets < K # Mask for real k.
485586 best_dist_key = tl .where (k_mask , best_dist_key , INF_KEY )
486- best_idx = (best_dist_key & 0xFFFFFFFF )
587+ best_idx = (best_dist_key & 0xFFFFFFFF ). to ( tl . int32 )
487588
488589 out_offsets = n_y * K + k_offsets # Output offsets.
489590 out_mask = mask_y & k_mask # Valid output mask.
@@ -616,6 +717,7 @@ def _radius_segmented_kernel(
616717 tl .max_contiguous (offs_n , 8 )
617718
618719 acc_dist = tl .zeros ((BLOCK_N ,), tl .float32 )
720+ acc_dist_c = tl .zeros ((BLOCK_N ,), tl .float32 )
619721 x_block_start_i32 = x_block_start .to (tl .int32 )
620722 x_block_ptr = tl .make_block_ptr (
621723 base = x_ptr ,
@@ -651,11 +753,19 @@ def _radius_segmented_kernel(
651753 )
652754
653755 diff = x - y
654- acc_dist += tl .sum (diff * diff , axis = 1 )
756+ term = tl .sum (diff * diff , axis = 1 )
757+ t_k = acc_dist + term
758+ k_cond = tl .abs (acc_dist ) >= tl .abs (term )
759+ acc_dist_c += tl .where (
760+ k_cond ,
761+ (acc_dist - t_k ) + term ,
762+ (term - t_k ) + acc_dist ,
763+ )
764+ acc_dist = t_k
655765 x_block_ptr = tl .advance (x_block_ptr , (0 , BLOCK_D ))
656766 y_row_ptr = tl .advance (y_row_ptr , (0 , BLOCK_D ))
657767
658- dist = acc_dist
768+ dist = acc_dist + acc_dist_c
659769 active = count < max_neighbors
660770 mask = mask_x & (dist < R2 ) & active
661771 if IGNORE_SAME_INDEX :
0 commit comments