@@ -380,6 +380,15 @@ __global__ void kern_fused_prune(KnnGraphView knn_graph, // [graph_chunk_
380380 }
381381}
382382
383+ // Cheap stateless integer hash (PCG-style mixing) used for deterministic, warp-uniform thinning of
384+ // reverse-graph edges during the merge step.
385+ __device__ __forceinline__ uint32_t mix_hash (uint32_t x)
386+ {
387+ x = x * 747796405u + 2891336453u ;
388+ uint32_t word = ((x >> ((x >> 28u ) + 4u )) ^ x) * 277803737u ;
389+ return (word >> 22u ) ^ word;
390+ }
391+
383392// Helper functions for merging the graph
384393template <typename T>
385394__device__ unsigned int warp_pos_in_array (T val, const T* array, uint64_t num)
@@ -534,7 +543,15 @@ __global__ void kern_merge_graph(
534543 if (rev_graph_value < graph_size) {
535544 if constexpr (VariableDegree) {
536545 const uint32_t in_degree = rev_graph_count (rev_graph_value);
537- if (std::min (in_degree, my_in_degree) + ed0 < output_graph_degree) { continue ; }
546+ if (my_in_degree < output_graph_degree * 3 || in_degree < output_graph_degree * 1 ) {
547+ // Don't prune highways (edges between high in-degree nodes)
548+ // P(skipping the edge) = a / b
549+ const uint32_t a = output_graph_degree + ed0;
550+ const uint32_t b = output_graph_degree * 2 ;
551+ const uint32_t r = mix_hash (static_cast <uint32_t >(nid) * 0x9e3779b9u ^
552+ static_cast <uint32_t >(rev_graph_value));
553+ if ((r % b) < a) { continue ; }
554+ }
538555 }
539556 uint64_t pos =
540557 warp_pos_in_array<IdxT>(rev_graph_value, smem_sorted_output_graph, output_graph_degree);
0 commit comments