ruprim/reduce/components/instructions/
topk.rs1use ruda_kernel::dsl as kernel_dsl;
2use ruda_kernel::dsl::comptime;
3use ruda_kernel::dsl::ruda;
4use ruda_kernel::dsl::frontend::RudaIndexMutExpand;
5use ruda_kernel::dsl::prelude::*;
6
7use crate::reduce::components::instructions::AccumulatorFormat;
8use crate::reduce::components::instructions::plane_topk_insert;
9use crate::reduce::components::instructions::plane_topk_merge;
10use crate::reduce::components::instructions::{Accumulator, Item, Value};
11use crate::reduce::{
12 ReduceFamily, ReduceInstruction, ReducePrecision,
13 components::instructions::{ReduceRequirements, ReduceStep, SharedAccumulator},
14};
15use ruda_kernel::dsl::frontend::Numeric;
16
17#[derive(Debug, RudaType, Clone)]
18pub struct TopK {
19 #[ruda(comptime)]
20 pub k: usize,
21}
22
23impl ReduceFamily for TopK {
24 type Instruction<P: ReducePrecision> = Self;
25 type Config = usize;
26}
27
28#[derive(RudaType)]
29pub struct TopkAccumulator<E: Scalar, S: Size> {
30 pub elements: Array<Vector<E, S>>,
31 pub coordinates: Array<Vector<u32, S>>,
32}
33
34#[derive(RudaType)]
35pub struct TopKSharedAccumulator<P: ReducePrecision> {
36 elements: Sequence<SharedMemory<Vector<P::EA, P::SI>>>,
37 #[ruda(comptime)]
38 k: usize,
39}
40
41#[ruda]
42impl<P: ReducePrecision> SharedAccumulator<P, TopK> for TopKSharedAccumulator<P> {
43 fn allocate(#[comptime] length: usize, #[comptime] _coordinate: bool, inst: &TopK) -> Self {
44 let mut elements = Sequence::new();
45 for _ in 0..inst.k {
46 elements.push(SharedMemory::new(length));
47 }
48 TopKSharedAccumulator::<P> {
49 elements,
50 k: inst.k,
51 }
52 }
53
54 fn read(accumulator: &Self, index: usize) -> Accumulator<P> {
55 let mut values = Array::new(accumulator.k);
56 #[unroll]
57 for i in 0..accumulator.k {
58 values[i] = accumulator.elements[i][index];
59 }
60 Accumulator::<P> {
61 elements: Value::new_Multiple(values),
62 args: Value::new_None(),
63 }
64 }
65
66 fn write(accumulator: &mut Self, index: usize, item: Accumulator<P>) {
67 let values = item.elements.multiple();
68 #[unroll]
69 for i in 0..accumulator.k {
70 let acc = values[i];
71 let mut shared_acc = accumulator.elements[i];
72 shared_acc[index] = acc;
73 }
74 }
75}
76
77#[ruda]
78impl<P: ReducePrecision> ReduceInstruction<P> for TopK {
79 type SharedAccumulator = TopKSharedAccumulator<P>;
80 type Config = usize;
81
82 fn requirements(_this: &Self) -> super::ReduceRequirements {
83 ReduceRequirements { coordinates: false }
84 }
85
86 fn accumulator_format(this: &Self) -> comptime_type!(AccumulatorFormat) {
87 comptime!(AccumulatorFormat::Multiple(this.k))
88 }
89
90 fn from_config(#[comptime] config: Self::Config) -> Self {
91 TopK { k: config }
92 }
93
94 fn null_input(_this: &Self) -> Vector<P::EI, P::SI> {
95 Vector::empty().fill(P::EI::min_value())
96 }
97
98 fn null_accumulator(this: &Self) -> Accumulator<P> {
99 let mut elements = Array::new(comptime!(this.k));
100 #[unroll]
101 for i in 0..this.k {
102 elements[i] = Vector::new(P::EA::min_value());
103 }
104
105 Accumulator::<P> {
106 elements: Value::new_Multiple(elements),
107 args: Value::new_None(),
108 }
109 }
110
111 fn reduce(
112 this: &Self,
113 accumulator: &mut Accumulator<P>,
114 item: Item<P>,
115 #[comptime] reduce_step: ReduceStep,
116 ) {
117 let elements = accumulator.elements.multiple_mut();
118
119 match reduce_step {
120 ReduceStep::Plane => {
121 plane_topk_insert::<P::EA, P::SI>(
122 elements,
123 &mut accumulator.args,
124 Vector::cast_from(item.elements),
125 &item.args,
126 this.k,
127 false,
128 );
129 }
130 ReduceStep::Identity => {
131 let mut insert_item = Vector::cast_from(item.elements);
132
133 for j in 0..this.k {
134 let acc_item = elements[j];
135 let keep = acc_item.greater_than(insert_item);
136
137 elements[j] = select_many(keep, acc_item, insert_item);
138 insert_item = select_many(keep, insert_item, acc_item);
139 }
140 }
141 }
142 }
143
144 fn plane_reduce_inplace(this: &Self, accumulator: &mut Accumulator<P>) {
145 plane_topk_merge(
146 accumulator.elements.multiple_mut(),
147 &mut accumulator.args,
148 this.k,
149 false,
150 );
151 }
152
153 fn fuse_accumulators(this: &Self, accumulator: &mut Accumulator<P>, other: &Accumulator<P>) {
154 let acc_elements = accumulator.elements.multiple_mut();
155 let other_elements = other.elements.multiple();
156
157 for i in 0..this.k {
158 let mut item = other_elements[i];
159 for j in 0..this.k {
160 let current_item = acc_elements[j];
161 let keep = current_item.greater_than(item);
162
163 let new_top_item = select_many(keep, current_item, item);
164 let new_rest_item = select_many(keep, item, current_item);
165
166 acc_elements[j] = new_top_item;
167 item = new_rest_item;
168 }
169 }
170 }
171
172 fn to_output_parallel<Out: Numeric>(
173 this: &Self,
174 accumulator: Accumulator<P>,
175 _shape_axis_reduce: usize,
176 ) -> Value<Out> {
177 let accumulators = accumulator.elements.multiple();
178 let vector_size = accumulators[0].size().comptime();
179
180 let mut topk = Array::new(this.k);
181 #[unroll]
182 for slot in 0..this.k {
183 topk[slot] = Out::min_value();
184 }
185
186 #[unroll]
187 for i in 0..this.k {
188 #[unroll]
189 for j in 0..vector_size {
190 let mut element = Out::cast_from(accumulators[i][j]);
191
192 #[unroll]
193 for slot in 0..this.k {
194 let current = topk[slot];
195
196 let keep = current > element;
197
198 topk[slot] = select(keep, current, element);
199 element = select(keep, element, current);
200 }
201 }
202 }
203
204 Value::new_Multiple(topk)
205 }
206
207 fn to_output_perpendicular<Out: Numeric>(
208 this: &Self,
209 accumulator: Accumulator<P>,
210 _shape_axis_reduce: usize,
211 ) -> Value<Vector<Out, P::SI>> {
212 let acc_values = accumulator.elements.multiple();
213 let mut output = Array::new(this.k);
214
215 #[unroll]
216 for i in 0..this.k {
217 output[i] = Vector::cast_from(acc_values[i]);
218 }
219
220 Value::new_Multiple(output)
221 }
222}