77
88
99def _prune_knn_configs (configs , args , ** kwargs ):
10- d = int (args .get ('D' , 0 ))
11- max_cand = int (args .get ('MAX_CAND' , 0 ))
10+ k_pad = int (args .get ("K" , 0 ))
1211 pruned = []
1312 for cfg in configs :
14- block_n = cfg .kwargs .get ('BLOCK_N' , 0 )
15- block_d = cfg .kwargs .get ('BLOCK_D' , 0 )
16- if max_cand > 0 and block_n > max_cand :
17- continue
18- if d > 0 and block_d > d :
13+ block_n = cfg .kwargs .get ("BLOCK_N" , 0 )
14+ if block_n < k_pad :
1915 continue
2016 pruned .append (cfg )
21- if not pruned :
22- pruned = [min (configs , key = lambda c : c .kwargs .get ('BLOCK_N' , 0 ))]
2317 return pruned
2418
2519
@@ -79,6 +73,7 @@ def _nearest_kernel(
7973 USE_BATCH : tl .constexpr ,
8074 COSINE : tl .constexpr ,
8175 EPS : tl .constexpr ,
76+ INPUT_PRECISION : tl .constexpr ,
8277 BLOCK_M : tl .constexpr ,
8378 BLOCK_N : tl .constexpr ,
8479 BLOCK_K : tl .constexpr ,
@@ -135,7 +130,6 @@ def _nearest_kernel(
135130 right = M
136131
137132 x_sq = tl .zeros ((BLOCK_N ,), dtype = tl .float32 ) # ||x||^2.
138- x_sq_c = tl .zeros ((BLOCK_N ,), dtype = tl .float32 )
139133 x_block_ptr_sq = tl .make_block_ptr (
140134 base = x_ptr ,
141135 shape = (D , N ),
@@ -153,11 +147,7 @@ def _nearest_kernel(
153147 boundary_check = (0 , 1 ),
154148 padding_option = "zero" ,
155149 )
156- x_term = tl .sum (x * x , axis = 0 )
157- x_y = x_term - x_sq_c
158- x_t = x_sq + x_y
159- x_sq_c = (x_t - x_sq ) - x_y
160- x_sq = x_t
150+ x_sq += tl .sum (x * x , axis = 0 )
161151 x_block_ptr_sq = tl .advance (x_block_ptr_sq , (BLOCK_K , 0 ))
162152 if COSINE :
163153 inv_x = tl .rsqrt (x_sq + EPS ) # 1/||x||.
@@ -177,9 +167,7 @@ def _nearest_kernel(
177167 full_y = y_start + BLOCK_M <= M # Full tile.
178168
179169 acc = tl .zeros ((BLOCK_M , BLOCK_N ), dtype = tl .float32 ) # Dot acc.
180- acc_c = tl .zeros ((BLOCK_M , BLOCK_N ), dtype = tl .float32 )
181170 y_sq = tl .zeros ((BLOCK_M ,), dtype = tl .float32 ) # ||y||^2.
182- y_sq_c = tl .zeros ((BLOCK_M ,), dtype = tl .float32 )
183171
184172 x_block_ptr = x_block_ptr_base
185173 y_block_ptr = tl .make_block_ptr (
@@ -211,16 +199,8 @@ def _nearest_kernel(
211199 boundary_check = (0 , 1 ),
212200 padding_option = "zero" ,
213201 )
214- dot_term = tl .dot (y , x , input_precision = "ieee" )
215- dot_y = dot_term - acc_c
216- dot_t = acc + dot_y
217- acc_c = (dot_t - acc ) - dot_y
218- acc = dot_t
219- y_term = tl .sum (y * y , axis = 1 )
220- y_y = y_term - y_sq_c
221- y_t = y_sq + y_y
222- y_sq_c = (y_t - y_sq ) - y_y
223- y_sq = y_t
202+ acc += tl .dot (y , x , input_precision = INPUT_PRECISION )
203+ y_sq += tl .sum (y * y , axis = 1 )
224204 y_block_ptr = tl .advance (y_block_ptr , (0 , BLOCK_K ))
225205 x_block_ptr = tl .advance (x_block_ptr , (BLOCK_K , 0 ))
226206
@@ -308,7 +288,7 @@ def _nearest_kernel(
308288 ),
309289 ],
310290 key = ['D' , 'MAX_CAND' ],
311- # prune_configs_by={'early_config_prune': _prune_knn_configs},
291+ prune_configs_by = {'early_config_prune' : _prune_knn_configs },
312292)
313293@triton .heuristics ({
314294 'NUM_D_BLOCKS' : lambda args : triton .cdiv (args ['D' ], args ['BLOCK_D' ]),
@@ -317,15 +297,15 @@ def _nearest_kernel(
317297 args ['BLOCK_N' ],
318298 ),
319299 'EVEN_D' : lambda args : args ['D' ] % args ['BLOCK_D' ] == 0 ,
300+ 'K_PAD' : lambda args : triton .next_power_of_2 (args ['K' ])
320301})
321302@triton .jit
322303def _knn_segmented_kernel (
323304 x_ptr ,
324305 y_ptr ,
325306 ptr_x_ptr ,
326- example_idx_ptr ,
327- row_ptr ,
328- col_ptr ,
307+ batch_y_ptr ,
308+ grid_ptr ,
329309 M ,
330310 N ,
331311 D ,
@@ -336,6 +316,8 @@ def _knn_segmented_kernel(
336316 stride_yd ,
337317 K : tl .constexpr ,
338318 K_PAD : tl .constexpr ,
319+ USE_BATCH : tl .constexpr ,
320+ INPUT_PRECISION : tl .constexpr ,
339321 BLOCK_N : tl .constexpr ,
340322 BLOCK_D : tl .constexpr ,
341323 NUM_D_BLOCKS : tl .constexpr ,
@@ -350,20 +332,22 @@ def _knn_segmented_kernel(
350332 mask_y = n_y < M # Mask for valid y.
351333
352334 k_offsets = tl .arange (0 , K_PAD ) # Offsets for padded top-k buffer.
353- k_mask = k_offsets < K # Mask for real k.
354- inf_bits = tl .full (
335+ INF_KEY = 0x7f800000ffffffff
336+
337+ best_dist_key = tl .full (
355338 (K_PAD ,),
356- float ("inf" ),
357- tl .float32 ,
358- ).to (tl .int32 , bitcast = True )
359- best_dist_bits = inf_bits # Best distances (bitcast).
360- best_idx = tl .full ((K_PAD ,), - 1 , tl .int32 ) # Best indices.
339+ INF_KEY ,
340+ tl .int64 ,
341+ )
361342
362- example_idx = tl .load (
363- example_idx_ptr + n_y ,
364- mask = mask_y ,
365- other = 0 ,
366- ) # Segment id.
343+ if USE_BATCH :
344+ example_idx = tl .load (
345+ batch_y_ptr + n_y ,
346+ mask = mask_y ,
347+ other = 0 ,
348+ ) # Segment id.
349+ else :
350+ example_idx = 0
367351 x_start = tl .load (
368352 ptr_x_ptr + example_idx ,
369353 mask = mask_y ,
@@ -376,7 +360,6 @@ def _knn_segmented_kernel(
376360 ) # Segment end.
377361 if COSINE :
378362 y_sq = tl .zeros ((1 ,), tl .float32 ) # ||y||^2 accumulator.
379- y_sq_c = tl .zeros ((1 ,), tl .float32 )
380363 y_norm_ptr = tl .make_block_ptr (
381364 base = y_ptr ,
382365 shape = (D , M ),
@@ -394,23 +377,10 @@ def _knn_segmented_kernel(
394377 boundary_check = (0 , 1 ),
395378 padding_option = "zero" ,
396379 ) # Tail D.
397- y_term = tl .sum (y * y , axis = 0 )
398- y_y = y_term - y_sq_c
399- y_t = y_sq + y_y
400- y_sq_c = (y_t - y_sq ) - y_y
401- y_sq = y_t
380+ y_sq += tl .sum (y * y , axis = 0 )
402381 y_norm_ptr = tl .advance (y_norm_ptr , (BLOCK_D , 0 ))
403382 y_rnorm = tl .rsqrt (y_sq + EPS ) # 1/||y|| for cosine.
404383
405- if stride_xm % 8 == 0 :
406- tl .multiple_of (stride_xm , 8 ) # Hint alignment.
407- if stride_xd % 8 == 0 :
408- tl .multiple_of (stride_xd , 8 )
409- if stride_ym % 8 == 0 :
410- tl .multiple_of (stride_ym , 8 )
411- if stride_yd % 8 == 0 :
412- tl .multiple_of (stride_yd , 8 )
413-
414384 for xb in range (MAX_X_BLOCKS ):
415385 x_block_start = x_start + xb * BLOCK_N # Start of x block.
416386 offs_n = tl .arange (0 , BLOCK_N ) # Offsets within block.
@@ -430,12 +400,11 @@ def _knn_segmented_kernel(
430400 tl .multiple_of (offs_n , 8 ) # Hint vectorization.
431401 tl .max_contiguous (offs_n , 8 )
432402
433- acc_dot = tl .zeros ((BLOCK_N ,), tl .float32 ) # Dot accumulator.
434- acc_dot_c = tl .zeros ((BLOCK_N ,), tl .float32 )
435- acc_x_sq = tl .zeros ((BLOCK_N ,), tl .float32 ) # ||x||^2 accumulator.
436- acc_x_sq_c = tl .zeros ((BLOCK_N ,), tl .float32 )
437- acc_dist = tl .zeros ((BLOCK_N ,), tl .float32 )
438- acc_dist_c = tl .zeros ((BLOCK_N ,), tl .float32 )
403+ if COSINE :
404+ acc_dot = tl .zeros ((BLOCK_N ,), tl .float32 ) # Dot accumulator.
405+ acc_x_sq = tl .zeros ((BLOCK_N ,), tl .float32 ) # ||x||^2 accumulator.
406+ else :
407+ acc_dist = tl .zeros ((BLOCK_N ,), tl .float32 )
439408 x_block_start_i32 = x_block_start .to (
440409 tl .int32
441410 ) # Block ptr needs int32 offsets.
@@ -483,28 +452,15 @@ def _knn_segmented_kernel(
483452 padding_option = "zero" ,
484453 )
485454 if COSINE :
486- prod = tl .dot (x , y , input_precision = 'ieee' ) # MxV dot.
487- dot_term = tl .sum (prod , axis = 1 )
488- dot_y = dot_term - acc_dot_c
489- dot_t = acc_dot + dot_y
490- acc_dot_c = (dot_t - acc_dot ) - dot_y
491- acc_dot = dot_t
492- x_term = tl .sum (x * x , axis = 1 )
493- x_y = x_term - acc_x_sq_c
494- x_t = acc_x_sq + x_y
495- acc_x_sq_c = (x_t - acc_x_sq ) - x_y
496- acc_x_sq = x_t
497- x_block_ptr = tl .advance (x_block_ptr , (0 , BLOCK_D ))
455+ prod = tl .dot (x , y , input_precision = INPUT_PRECISION ) # MxV dot.
456+ acc_dot += tl .sum (prod , axis = 1 )
457+ acc_x_sq += tl .sum (x * x , axis = 1 )
498458 y_block_ptr = tl .advance (y_block_ptr , (BLOCK_D , 0 ))
499459 else :
500460 diff = x - y
501- term = tl .sum (diff * diff , axis = 1 )
502- y_k = term - acc_dist_c
503- t_k = acc_dist + y_k
504- acc_dist_c = (t_k - acc_dist ) - y_k
505- acc_dist = t_k
506- x_block_ptr = tl .advance (x_block_ptr , (0 , BLOCK_D ))
461+ acc_dist += tl .sum (diff * diff , axis = 1 )
507462 y_block_ptr = tl .advance (y_block_ptr , (0 , BLOCK_D ))
463+ x_block_ptr = tl .advance (x_block_ptr , (0 , BLOCK_D ))
508464 if COSINE :
509465 x_rnorm = tl .rsqrt (acc_x_sq + EPS ) # 1/||x||.
510466 dist = 1.0 - acc_dot * (x_rnorm * y_rnorm ) # Cosine distance.
@@ -520,53 +476,35 @@ def _knn_segmented_kernel(
520476 same_idx = same_idx & mask_x
521477 dist = tl .where (same_idx , float ("inf" ), dist )
522478
523-
524479 dist_bits = dist .to (tl .int32 , bitcast = True )
525480 dist_bits = dist_bits .to (tl .int64 ) # Pack dist.
526481 idx_bits = x_idx .to (tl .int32 ).to (tl .int64 ) & 0xFFFFFFFF # Pack idx.
527482 key = (dist_bits << 32 ) | idx_bits # Lexicographic key.
528483 sorted_key = tl .sort (key , descending = False ) # Full block sort.
529-
530- k_valid = k_offsets < K # Only first k entries are valid.
531484 key_k = tl .gather (sorted_key , k_offsets , axis = 0 ) # Take first k keys.
532- dist_k_bits = (key_k >> 32 ).to (tl .int32 ) # Unpack dist bits.
533- idx_k = (key_k & 0xFFFFFFFF ).to (tl .int32 ) # Unpack idx.
534- block_top_bits = tl .where (k_valid , dist_k_bits , inf_bits )
535- block_top_idx = tl .where (k_valid , idx_k , - 1 ) # Block top-k idx.
536-
537- shift_idx = k_offsets - K # Shift window for block top-k.
538- shift_valid = (k_offsets >= K ) & (k_offsets < (2 * K ))
539- shift_idx_safe = tl .where (shift_valid , shift_idx , 0 )
540- shift_bits = tl .gather (block_top_bits , shift_idx_safe , axis = 0 )
541- block_shifted_bits = tl .where (shift_valid , shift_bits , inf_bits )
542- shift_idx_val = tl .gather (block_top_idx , shift_idx_safe , axis = 0 )
543- block_shifted_idx = tl .where (shift_valid , shift_idx_val , - 1 )
544- combo_bits = tl .where (
545- k_offsets < K ,
546- best_dist_bits ,
547- block_shifted_bits ,
485+
486+ combo_key = tl .cat (
487+ best_dist_key ,
488+ key_k ,
489+ can_reorder = True
548490 ) # Merge buffers.
549- combo_idx = tl .where (k_offsets < K , best_idx , block_shifted_idx )
550- dist_bits = combo_bits .to (tl .int64 ) # Pack combo.
551- idx_bits = combo_idx .to (tl .int32 ).to (tl .int64 ) & 0xFFFFFFFF
552- combo_key = (dist_bits << 32 ) | idx_bits
553491 sorted_key = tl .sort (combo_key , descending = False ) # Sort 2K keys.
554- best_key = tl .gather (sorted_key , k_offsets , axis = 0 ) # Keep best K.
555- best_dist_bits = (best_key >> 32 ).to (tl .int32 )
556- best_idx = (best_key & 0xFFFFFFFF ).to (tl .int32 )
557-
558- best_dist = best_dist_bits .to (tl .float32 , bitcast = True )
559- best_idx = tl .where (
560- libdevice .isinf (best_dist ),
561- - 1 ,
562- best_idx ,
563- ) # Mask invalid.
564-
565- out_offsets = n_y * K + tl .where (k_mask , k_offsets , 0 ) # Output offsets.
492+ best_dist_key = tl .gather (sorted_key , k_offsets , axis = 0 ) # Keep best K.
493+
494+ k_mask = k_offsets < K # Mask for real k.
495+ best_dist_key = tl .where (k_mask , best_dist_key , INF_KEY )
496+ best_idx = (best_dist_key & 0xFFFFFFFF )
497+
498+ out_offsets = n_y * K + k_offsets # Output offsets.
566499 out_mask = mask_y & k_mask # Valid output mask.
567- tl .store (row_ptr + out_offsets , n_y , mask = out_mask ) # Write row ids.
500+
501+ tl .store (
502+ grid_ptr + out_offsets ,
503+ n_y ,
504+ mask = out_mask ,
505+ )
568506 tl .store (
569- col_ptr + out_offsets ,
507+ grid_ptr + M * K + out_offsets ,
570508 best_idx .to (tl .int64 ),
571509 mask = out_mask ,
572510 ) # Write col idx.
@@ -689,7 +627,6 @@ def _radius_segmented_kernel(
689627 tl .max_contiguous (offs_n , 8 )
690628
691629 acc_dist = tl .zeros ((BLOCK_N ,), tl .float32 )
692- acc_dist_c = tl .zeros ((BLOCK_N ,), tl .float32 )
693630 x_block_start_i32 = x_block_start .to (tl .int32 )
694631 x_block_ptr = tl .make_block_ptr (
695632 base = x_ptr ,
@@ -725,16 +662,13 @@ def _radius_segmented_kernel(
725662 )
726663
727664 diff = x - y
728- term = tl .sum (diff * diff , axis = 1 )
729- y_k = term - acc_dist_c
730- t_k = acc_dist + y_k
731- acc_dist_c = (t_k - acc_dist ) - y_k
732- acc_dist = t_k
665+ acc_dist += tl .sum (diff * diff , axis = 1 )
733666 x_block_ptr = tl .advance (x_block_ptr , (0 , BLOCK_D ))
734667 y_row_ptr = tl .advance (y_row_ptr , (0 , BLOCK_D ))
735668
669+ dist = acc_dist
736670 active = count < max_neighbors
737- mask = mask_x & (acc_dist < R2 ) & active
671+ mask = mask_x & (dist < R2 ) & active
738672 if IGNORE_SAME_INDEX :
739673 mask &= x_idx != n_y .to (tl .int64 )
740674
0 commit comments