1use super::{AutotuneError, AutotuneKey, TuneFn, TuneInputs};
2
3use alloc::boxed::Box;
4use alloc::string::ToString;
5use alloc::{string::String, sync::Arc, vec, vec::Vec};
6use core::sync::atomic::{AtomicU32, Ordering};
7use cubecl_environment::collections::HashMap;
8
9pub struct Tunable<K, F: TuneInputs, Output> {
12 pub(crate) function: TuneFn<F, Output>,
13 groups: Vec<(TuneGroup<K>, PriorityFunc<K>)>,
14}
15
16impl<K, F: TuneInputs, Output: 'static> Tunable<K, F, Output> {
17 pub fn new<Func, Err>(name: &str, func: Func) -> Self
31 where
32 Err: Into<String> + 'static,
33 Func: for<'a> Fn(<F as TuneInputs>::At<'a>) -> Result<Output, Err> + Send + Sync + 'static,
34 {
35 let name: String = name.into();
36 let name_for_err = name.clone();
37 Self {
38 function: TuneFn::new(
39 name,
40 Box::new(move |inputs| {
41 func(inputs).map_err(|err| AutotuneError::Unknown {
42 name: name_for_err.to_string(),
43 err: err.into(),
44 })
45 }),
46 ),
47 groups: Vec::new(),
48 }
49 }
50
51 pub fn group(
58 mut self,
59 group: &TuneGroup<K>,
60 priority: impl Fn(&K) -> i8 + Send + Sync + 'static,
61 ) -> Self {
62 self.groups.push((group.clone(), Arc::new(priority)));
63 self
64 }
65}
66
67pub struct TuneGroup<K> {
72 id: u32,
73 name: Arc<String>,
74 pub(crate) priority: PriorityFunc<K>,
75 ordered: bool,
77}
78
79impl<K> core::fmt::Debug for TuneGroup<K> {
80 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81 f.debug_struct("TuneGroup")
82 .field("id", &self.id)
83 .field("name", &self.name)
84 .finish()
85 }
86}
87
88impl<K> Clone for TuneGroup<K> {
89 fn clone(&self) -> Self {
90 Self {
91 id: self.id,
92 name: self.name.clone(),
93 priority: self.priority.clone(),
94 ordered: self.ordered,
95 }
96 }
97}
98
99impl<K> TuneGroup<K> {
100 pub fn new(name: &str, f: impl Fn(&K) -> i8 + Send + Sync + 'static) -> Self {
105 Self::build(name, f, false)
106 }
107
108 pub fn ordered(name: &str, f: impl Fn(&K) -> i8 + Send + Sync + 'static) -> Self {
120 Self::build(name, f, true)
121 }
122
123 fn build(name: &str, f: impl Fn(&K) -> i8 + Send + Sync + 'static, ordered: bool) -> Self {
124 let id = GROUP_COUNTER.fetch_add(1, Ordering::Relaxed);
125
126 Self {
127 id,
128 name: Arc::new(name.into()),
129 priority: Arc::new(f),
130 ordered,
131 }
132 }
133}
134
135#[derive(Debug)]
136pub(crate) struct TunePlan {
138 priorities: Vec<i8>,
139 no_groups: Vec<usize>,
140 groups: HashMap<i8, GroupPlan>,
141 returned: Vec<usize>,
142}
143
144#[derive(Default, Debug)]
145struct GroupPlan {
146 priorities: Vec<i8>,
147 indices: HashMap<i8, Vec<Planned>>,
148}
149
150#[derive(Debug)]
157struct Planned {
158 index: usize,
159 group: u32,
160 priority: i8,
161}
162
163#[derive(Debug)]
164struct Cleanup {
165 groups: Vec<i8>,
166 tunables: Vec<(i8, i8)>,
167 skipped: bool,
169}
170
171impl TunePlan {
172 pub fn new<K: AutotuneKey, F: TuneInputs, Out>(
173 key: &K,
174 tunables: &[Tunable<K, F, Out>],
175 ) -> Self {
176 let mut priorities = Vec::<i8>::new();
177 let mut no_groups = Vec::new();
178 let mut groups = HashMap::<i8, GroupPlan>::new();
179
180 let mut priced = Vec::new();
184 let mut ordered_levels = HashMap::<u32, i8>::new();
185
186 for (index, tunable) in tunables.iter().enumerate() {
187 if tunable.groups.is_empty() {
188 no_groups.push(index);
189 continue;
190 }
191
192 for (group, within_group_priority_fn) in tunable.groups.iter() {
193 let group_priority = (group.priority)(key);
194 let priority = within_group_priority_fn(key);
195
196 if group.ordered && priority >= 0 {
197 let level = ordered_levels.entry(group.id).or_insert(priority);
198 *level = (*level).max(priority);
199 }
200
201 priced.push((index, group, group_priority, priority));
202 }
203 }
204
205 for (index, group, group_priority, priority) in priced {
206 if !priorities.contains(&group_priority) {
207 priorities.push(group_priority);
208 }
209
210 let group_plan = match groups.get_mut(&group_priority) {
211 Some(val) => val,
212 None => {
213 groups.insert(group_priority, GroupPlan::default());
214 groups.get_mut(&group_priority).unwrap()
215 }
216 };
217
218 let level = match group.ordered && priority >= 0 {
221 true => ordered_levels[&group.id],
222 false => priority,
223 };
224 let planned = Planned {
225 index,
226 group: group.id,
227 priority,
228 };
229
230 if group_plan.priorities.contains(&level) {
231 group_plan.indices.get_mut(&level).unwrap().push(planned);
232 } else {
233 group_plan.priorities.push(level);
234 group_plan.indices.insert(level, vec![planned]);
235 }
236 }
237
238 priorities.sort();
239
240 for group in groups.iter_mut() {
241 group.1.priorities.sort();
242 }
243
244 Self {
245 priorities,
246 no_groups,
247 groups,
248 returned: Vec::new(),
249 }
250 }
251
252 pub(crate) fn next(&mut self) -> Vec<usize> {
256 let ungrouped = core::mem::take(&mut self.no_groups);
259 let mut indices = Vec::new();
260 let priority = self.priorities.last();
261
262 let priority = match priority {
263 Some(val) => *val,
264 None => return ungrouped,
265 };
266
267 let (group_indices, cleanup) = self.group_plan_next(priority);
268 let skipped = cleanup.skipped || priority < 0;
270 let mut all_skip = true;
271
272 self.cleanup(cleanup);
273
274 if priority >= 0 {
275 for index in group_indices {
276 if !self.returned.contains(&index) && !indices.contains(&index) {
277 all_skip = false;
278 indices.push(index);
279 }
280 }
281 }
282
283 indices.extend(ungrouped);
284
285 if indices.is_empty() && (skipped || all_skip) {
289 self.next()
290 } else {
291 for i in indices.iter() {
292 self.returned.push(*i);
293 }
294 indices
295 }
296 }
297
298 fn cleanup(&mut self, cleanup: Cleanup) {
299 for group_p in cleanup.groups {
300 let index = self
301 .priorities
302 .iter()
303 .enumerate()
304 .find(|p| *p.1 == group_p)
305 .unwrap();
306
307 self.priorities.remove(index.0);
308 self.groups.remove(&group_p);
309 }
310
311 for (group_p, tunable_p) in cleanup.tunables {
312 if let Some(group) = self.groups.get_mut(&group_p) {
313 let index = group
314 .priorities
315 .iter()
316 .enumerate()
317 .find(|p| *p.1 == tunable_p)
318 .unwrap();
319 group.priorities.remove(index.0);
320 group.indices.remove(&tunable_p);
321 }
322 }
323 }
324
325 fn group_plan_next(&mut self, priority: i8) -> (Vec<usize>, Cleanup) {
326 let group_plan = self.groups.get_mut(&priority).expect("To be filled");
327 let within_group_prio = group_plan.priorities.pop().unwrap();
328 let mut next_indices = group_plan.indices.remove(&within_group_prio).unwrap();
329 next_indices.sort_by_key(|planned| (core::cmp::Reverse(planned.priority), planned.index));
331
332 let mut cleanup_groups = Vec::new();
333 let mut cleanup_tunables = Vec::new();
334
335 for (pg, group) in self.groups.iter_mut() {
336 let mut num_empty_tunables = 0;
337 let num_tunables = group.priorities.len();
338
339 for (pt, indices) in group.indices.iter_mut() {
340 for n in &next_indices {
341 let entry = indices
342 .iter()
343 .position(|p| p.index == n.index && p.group == n.group);
344 if let Some(entry) = entry {
345 indices.remove(entry);
346 }
347 }
348
349 if indices.is_empty() {
350 num_empty_tunables += 1;
351 cleanup_tunables.push((*pg, *pt));
352 }
353 }
354
355 if num_empty_tunables == num_tunables {
356 cleanup_groups.push(*pg);
357 }
358 }
359
360 if within_group_prio < 0 {
361 next_indices.clear();
363 }
364
365 (
366 next_indices
367 .into_iter()
368 .map(|planned| planned.index)
369 .collect(),
370 Cleanup {
371 groups: cleanup_groups,
372 tunables: cleanup_tunables,
373 skipped: within_group_prio < 0,
374 },
375 )
376 }
377}
378
379type PriorityFunc<K> = Arc<dyn Fn(&K) -> i8 + Send + Sync>;
380
381static GROUP_COUNTER: AtomicU32 = AtomicU32::new(0);
382
383#[cfg(test)]
384mod tests {
385 use core::fmt::Display;
386
387 use serde::{Deserialize, Serialize};
388
389 use super::*;
390
391 #[derive(Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)]
392 struct FakeAutotuneKey;
393
394 impl Display for FakeAutotuneKey {
395 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
396 f.write_str("FakeAutotuneKey")
397 }
398 }
399
400 impl AutotuneKey for FakeAutotuneKey {}
401
402 #[test_log::test]
403 fn test_plan_order() {
404 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 2);
405 let group1 = TuneGroup::<FakeAutotuneKey>::new("group1", |_| 1);
406
407 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel);
408 let tunable1 =
409 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 1);
410 let tunable2 =
411 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 2);
412 let tunable3 =
413 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group1, |_| 2);
414
415 let key = FakeAutotuneKey;
416 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2, tunable3]);
417
418 assert_eq!(plan.next(), vec![2, 0]);
420 assert_eq!(plan.next(), vec![1]);
421 assert_eq!(plan.next(), vec![3]);
422 assert!(plan.next().is_empty());
423 }
424
425 #[test_log::test]
426 fn test_plan_order_multi_groups_same_priority() {
427 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 2);
428 let group1 = TuneGroup::<FakeAutotuneKey>::new("group1", |_| 1);
429 let group2 = TuneGroup::<FakeAutotuneKey>::new("group2", |_| 1);
430
431 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel);
432 let tunable1 =
433 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 1);
434 let tunable2 =
435 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 2);
436 let tunable3 =
437 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group1, |_| 2);
438 let tunable4 =
439 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group2, |_| 2);
440
441 let key = FakeAutotuneKey;
442 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2, tunable3, tunable4]);
443
444 assert_eq!(plan.next(), vec![2, 0]);
445 assert_eq!(plan.next(), vec![1]);
446 assert_eq!(plan.next(), vec![3, 4]);
447 assert!(plan.next().is_empty());
448 }
449
450 #[test_log::test]
451 fn test_plan_order_tunable_multiple_groups() {
452 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 1);
453 let group1 = TuneGroup::<FakeAutotuneKey>::new("group1", |_| 2);
454
455 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel);
456 let tunable1 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel)
457 .group(&group0, |_| 1)
458 .group(&group1, |_| 2);
459 let tunable2 =
460 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 2);
461 let tunable3 =
462 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group1, |_| 3);
463
464 let key = FakeAutotuneKey;
465 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2, tunable3]);
466
467 assert_eq!(plan.next(), vec![3, 0]);
468 assert_eq!(plan.next(), vec![1]);
469 assert_eq!(plan.next(), vec![2]);
470 assert!(plan.next().is_empty());
471 }
472
473 #[test_log::test]
474 fn test_plan_negative_priority() {
475 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 2);
476 let group1 = TuneGroup::<FakeAutotuneKey>::new("group1", |_| 1);
477
478 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel);
479 let tunable1 =
480 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| -1);
481 let tunable2 =
482 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 2);
483 let tunable3 =
484 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group1, |_| 2);
485
486 let key = FakeAutotuneKey;
487 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2, tunable3]);
488
489 assert_eq!(plan.next(), vec![2, 0]);
490 assert_eq!(plan.next(), vec![3]);
491 assert!(plan.next().is_empty());
492 }
493
494 #[test_log::test]
495 fn test_plan_no_group() {
496 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel);
497 let tunable1 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel);
498
499 let key = FakeAutotuneKey;
500 let mut plan = TunePlan::new(&key, &[tunable0, tunable1]);
501
502 assert_eq!(plan.next(), vec![0, 1]);
503 assert!(plan.next().is_empty());
504 }
505
506 #[test_log::test]
507 fn test_plan_falls_through_when_all_group_tunables_fail() {
508 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 2);
512 let group1 = TuneGroup::<FakeAutotuneKey>::new("group1", |_| 1);
513
514 let tunable0 =
515 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 1);
516 let tunable1 =
517 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 2);
518 let tunable2 =
519 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group1, |_| 1);
520 let tunable3 =
521 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group1, |_| 2);
522
523 let key = FakeAutotuneKey;
524 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2, tunable3]);
525
526 let mut all_returned: Vec<usize> = Vec::new();
527 loop {
528 let batch = plan.next();
529 if batch.is_empty() {
530 break;
531 }
532 all_returned.extend(batch);
533 }
534
535 assert_eq!(all_returned, vec![1, 0, 3, 2]);
537 }
538
539 #[test_log::test]
540 fn test_plan_single_group_exhausts_all_intra_priorities() {
541 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 0);
544
545 let tunable0 =
546 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 1);
547 let tunable1 =
548 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 2);
549 let tunable2 =
550 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 3);
551
552 let key = FakeAutotuneKey;
553 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2]);
554
555 assert_eq!(plan.next(), vec![2]);
556 assert_eq!(plan.next(), vec![1]);
557 assert_eq!(plan.next(), vec![0]);
558 assert!(plan.next().is_empty());
559 }
560
561 #[test_log::test]
562 fn test_plan_all_negative_group_advances_to_next_group() {
563 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 2);
566 let group1 = TuneGroup::<FakeAutotuneKey>::new("group1", |_| 1);
567
568 let tunable0 =
569 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| -1);
570 let tunable1 =
571 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| -2);
572 let tunable2 =
573 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group1, |_| 1);
574
575 let key = FakeAutotuneKey;
576 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2]);
577
578 assert_eq!(plan.next(), vec![2]);
579 assert!(plan.next().is_empty());
580 }
581
582 #[test_log::test]
583 fn test_plan_no_group_tunables_only_emitted_once_even_on_failures() {
584 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 2);
588 let group1 = TuneGroup::<FakeAutotuneKey>::new("group1", |_| 1);
589
590 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel);
591 let tunable1 =
592 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 1);
593 let tunable2 =
594 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group1, |_| 1);
595
596 let key = FakeAutotuneKey;
597 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2]);
598
599 assert_eq!(plan.next(), vec![1, 0]);
600 assert_eq!(plan.next(), vec![2]);
601 assert!(plan.next().is_empty());
602 }
603
604 #[test_log::test]
605 fn test_plan_multi_group_tunable_not_duplicated_across_failed_groups() {
606 let group0 = TuneGroup::<FakeAutotuneKey>::new("group0", |_| 1);
609 let group1 = TuneGroup::<FakeAutotuneKey>::new("group1", |_| 2);
610
611 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel)
612 .group(&group0, |_| 1)
613 .group(&group1, |_| 1);
614 let tunable1 =
615 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group0, |_| 2);
616
617 let key = FakeAutotuneKey;
618 let mut plan = TunePlan::new(&key, &[tunable0, tunable1]);
619
620 let mut all_returned: Vec<usize> = Vec::new();
621 loop {
622 let batch = plan.next();
623 if batch.is_empty() {
624 break;
625 }
626 all_returned.extend(batch);
627 }
628
629 assert_eq!(all_returned, vec![0, 1]);
632 }
633
634 #[test_log::test]
635 fn test_plan_recurses_when_batch_is_fully_already_returned() {
636 let group_hi = TuneGroup::<FakeAutotuneKey>::new("hi", |_| 2);
646 let group_lo = TuneGroup::<FakeAutotuneKey>::new("lo", |_| 1);
647
648 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel)
650 .group(&group_hi, |_| 1)
651 .group(&group_lo, |_| 2);
652 let tunable1 =
653 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group_lo, |_| 1);
654
655 let key = FakeAutotuneKey;
656 let mut plan = TunePlan::new(&key, &[tunable0, tunable1]);
657
658 assert_eq!(plan.next(), vec![0]);
660 assert_eq!(plan.next(), vec![1]);
664 assert!(plan.next().is_empty());
665 }
666
667 #[test_log::test]
668 fn test_plan_ordered_group_is_one_batch_best_first() {
669 let group = TuneGroup::<FakeAutotuneKey>::ordered("ordered", |_| 1);
671
672 let tunable0 =
673 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group, |_| 1);
674 let tunable1 =
675 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group, |_| 3);
676 let tunable2 =
677 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group, |_| -1);
678 let tunable3 =
679 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group, |_| 3);
680 let tunable4 =
681 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group, |_| 2);
682
683 let key = FakeAutotuneKey;
684 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2, tunable3, tunable4]);
685
686 assert_eq!(plan.next(), vec![1, 3, 4, 0]);
687 assert!(plan.next().is_empty());
688 }
689
690 #[test_log::test]
691 fn test_plan_ordered_group_keeps_the_group_cutoff() {
692 let first = TuneGroup::<FakeAutotuneKey>::ordered("first", |_| 2);
695 let fallback = TuneGroup::<FakeAutotuneKey>::new("fallback", |_| 1);
696
697 let tunable0 =
698 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&first, |_| 1);
699 let tunable1 =
700 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&first, |_| 2);
701 let tunable2 =
702 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&fallback, |_| 1);
703
704 let key = FakeAutotuneKey;
705 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2]);
706
707 assert_eq!(plan.next(), vec![1, 0]);
708 assert_eq!(plan.next(), vec![2]);
709 assert!(plan.next().is_empty());
710 }
711
712 #[test_log::test]
713 fn test_plan_ordered_batch_leads_the_ungrouped_tunables() {
714 let group = TuneGroup::<FakeAutotuneKey>::ordered("ordered", |_| 1);
718
719 let tunable0 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel);
720 let tunable1 =
721 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group, |_| 1);
722 let tunable2 =
723 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&group, |_| 5);
724
725 let key = FakeAutotuneKey;
726 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2]);
727
728 assert_eq!(plan.next(), vec![2, 1, 0]);
729 assert!(plan.next().is_empty());
730 }
731
732 #[test_log::test]
733 fn test_plan_ordered_batch_is_not_jumped_by_a_cutoff_group_beside_it() {
734 let ordered = TuneGroup::<FakeAutotuneKey>::ordered("ordered", |_| 1);
739 let cutoff = TuneGroup::<FakeAutotuneKey>::new("cutoff", |_| 1);
740
741 let tunable0 =
742 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&ordered, |_| 3);
743 let tunable1 =
744 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&ordered, |_| 1);
745 let tunable2 =
746 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&cutoff, |_| 2);
747
748 let key = FakeAutotuneKey;
749 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2]);
750
751 assert_eq!(plan.next(), vec![0, 1]);
752 assert_eq!(plan.next(), vec![2]);
753 assert!(plan.next().is_empty());
754 }
755
756 #[test_log::test]
757 fn test_plan_cutoff_member_above_the_ordered_batch_still_leads() {
758 let ordered = TuneGroup::<FakeAutotuneKey>::ordered("ordered", |_| 1);
762 let cutoff = TuneGroup::<FakeAutotuneKey>::new("cutoff", |_| 1);
763
764 let tunable0 =
765 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&ordered, |_| 2);
766 let tunable1 =
767 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&ordered, |_| 1);
768 let tunable2 =
769 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&cutoff, |_| 3);
770 let tunable3 =
771 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&cutoff, |_| 2);
772
773 let key = FakeAutotuneKey;
774 let mut plan = TunePlan::new(&key, &[tunable0, tunable1, tunable2, tunable3]);
775
776 assert_eq!(plan.next(), vec![2]);
777 assert_eq!(plan.next(), vec![0, 3, 1]);
778 assert!(plan.next().is_empty());
779 }
780
781 #[test_log::test]
782 fn test_plan_same_named_groups_do_not_strike_each_other_out() {
783 let hi = TuneGroup::<FakeAutotuneKey>::new("shared", |_| 2);
787 let lo = TuneGroup::<FakeAutotuneKey>::new("shared", |_| 1);
788
789 let tunable0 =
790 Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel).group(&hi, |_| 1);
791 let tunable1 = Tunable::<FakeAutotuneKey, (), ()>::new("fake", fake_kernel)
792 .group(&hi, |_| -1)
793 .group(&lo, |_| 1);
794
795 let key = FakeAutotuneKey;
796 let mut plan = TunePlan::new(&key, &[tunable0, tunable1]);
797
798 assert_eq!(plan.next(), vec![0]);
799 assert_eq!(plan.next(), vec![1]);
800 assert!(plan.next().is_empty());
801 }
802
803 fn fake_kernel(_: ()) -> Result<(), String> {
804 Ok(())
805 }
806}