cubecl_runtime/tune/
operation.rs1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::sync::Arc;
4use alloc::vec::Vec;
5use core::fmt::{Debug, Display};
6use core::hash::Hash;
7use cubecl_common::hash::{StableHash, StableHasher};
8
9use alloc::format;
10
11use crate::tune::{Bounds, BoundsGenerator, Eviction, Evictor};
12
13use super::{
14 AutotuneError, input_generator::InputGenerator, key_generator::KeyGenerator,
15 tune_inputs::TuneInputs,
16};
17use super::{Tunable, TunePlan};
18
19type TuneDelegate<I, Out> =
24 dyn for<'inp> Fn(<I as TuneInputs>::At<'inp>) -> Result<Out, AutotuneError> + Send + Sync;
25
26#[derive(new)]
29pub struct TuneFn<I: TuneInputs, Out> {
30 pub(crate) name: String,
31 func: Box<TuneDelegate<I, Out>>,
32}
33
34impl<I: TuneInputs, Out: 'static> TuneFn<I, Out> {
35 pub fn execute<'a>(&self, inputs: <I as TuneInputs>::At<'a>) -> Result<Out, AutotuneError> {
37 (self.func)(inputs)
38 }
39}
40
41pub struct TunableSet<K: AutotuneKey, F: TuneInputs, Output: 'static, Id = ()> {
47 tunables: Vec<Tunable<K, F, Output, Id>>,
48 checksum: u64,
51 key_gen: Arc<dyn KeyGenerator<K, F> + Send + Sync>,
52 input_gen: Arc<dyn InputGenerator<K, F> + Send + Sync>,
53 bounds_gen: Option<Arc<dyn BoundsGenerator<K, F> + Send + Sync>>,
54 eviction: Option<Arc<dyn Eviction<K, F> + Send + Sync>>,
55 short_circuit: bool,
56}
57
58impl<K: AutotuneKey, F: TuneInputs, Output: 'static, Id> TunableSet<K, F, Output, Id> {
59 pub fn new(key_gen: impl KeyGenerator<K, F>, input_gen: impl InputGenerator<K, F>) -> Self {
61 let mut set = Self {
62 tunables: Default::default(),
63 checksum: 0,
64 input_gen: Arc::new(input_gen),
65 key_gen: Arc::new(key_gen),
66 bounds_gen: None,
67 eviction: None,
68 short_circuit: true,
69 };
70 set.checksum = set.names_hash() as u64;
71 set
72 }
73
74 pub fn new_cloning_inputs(key_gen: impl KeyGenerator<K, F>) -> Self {
77 Self::new(key_gen, super::CloneInputGenerator)
78 }
79
80 pub fn len(&self) -> usize {
82 self.tunables.len()
83 }
84
85 pub fn is_empty(&self) -> bool {
87 self.tunables.is_empty()
88 }
89
90 pub fn with(mut self, tunable: Tunable<K, F, Output, Id>) -> Self {
92 self.tunables.push(tunable);
93 self.checksum = self.names_hash() as u64;
94 self
95 }
96
97 pub fn with_bounds(mut self, bounds: Arc<dyn BoundsGenerator<K, F> + Send + Sync>) -> Self {
99 self.bounds_gen = Some(bounds);
100 self
101 }
102
103 pub fn with_eviction(mut self, eviction: Arc<dyn Eviction<K, F> + Send + Sync>) -> Self {
106 self.eviction = Some(eviction);
107 self
108 }
109
110 pub fn with_short_circuit(mut self, enabled: bool) -> Self {
112 self.short_circuit = enabled;
113 self
114 }
115
116 pub fn is_short_circuit_enabled(&self) -> bool {
118 self.short_circuit
119 }
120
121 pub fn autotunables(&self) -> impl Iterator<Item = &TuneFn<F, Output>> {
123 self.tunables.iter().map(|tunable| &tunable.function)
124 }
125
126 pub(crate) fn plan(&self, key: &K) -> TunePlan {
128 TunePlan::new(key, &self.tunables)
129 }
130
131 pub fn fastest(&self, fastest_index: usize) -> &TuneFn<F, Output> {
134 &self.tunables[fastest_index].function
135 }
136
137 pub(crate) fn identity(&self, index: usize) -> &Id {
141 self.tunables[index].identity()
142 }
143
144 pub fn compute_checksum(&self) -> String {
147 format!("{:x}", self.names_hash())
148 }
149
150 pub(crate) fn checksum(&self) -> u64 {
152 self.checksum
153 }
154
155 fn names_hash(&self) -> StableHash {
156 let mut hasher = StableHasher::new();
158 for tune in &self.tunables {
159 tune.function.name.hash(&mut hasher);
160 }
161 hasher.finalize()
162 }
163
164 pub fn generate_key<'a>(&self, inputs: &F::At<'a>) -> K {
166 self.key_gen.generate(inputs)
167 }
168
169 pub fn generate_inputs<'a>(&self, key: &K, inputs: &F::At<'a>) -> F::At<'a> {
171 self.input_gen.generate(key, inputs)
172 }
173
174 pub(crate) fn evictor<'i>(&self, key: &K, inputs: &F::At<'i>) -> Option<Box<Evictor<'i>>> {
177 let eviction = self.eviction.clone()?;
178 let key = key.clone();
179 let inputs = inputs.clone();
180 Some(Box::new(move || eviction.evict(&key, &inputs)))
181 }
182
183 pub fn bounds(&self, key: &K, inputs: &F::At<'_>) -> Option<Bounds> {
185 self.bounds_gen.as_ref().map(|f| f.generate(key, inputs))
186 }
187}
188
189#[cfg(serializable)]
190pub trait AutotuneKey:
192 Clone
193 + Debug
194 + PartialEq
195 + Eq
196 + Hash
197 + Display
198 + serde::Serialize
199 + serde::de::DeserializeOwned
200 + Send
201 + Sync
202 + 'static
203{
204}
205#[cfg(not(serializable))]
206pub trait AutotuneKey:
208 Clone + Debug + PartialEq + Eq + Hash + Display + Send + Sync + 'static
209{
210}
211
212impl AutotuneKey for String {}