burn_compute/tune/
tune_cache.rs1#[cfg(feature = "autotune-persistent-cache")]
2mod std_imports {
3 pub use std::fs;
4 pub use std::fs::File;
5 pub use std::io;
6 pub use std::path::Path;
7 pub use std::path::PathBuf;
8}
9#[cfg(feature = "autotune-persistent-cache")]
10use std_imports::*;
11
12#[cfg(feature = "autotune-persistent-cache")]
13use serde::{Deserialize, Serialize};
14
15use super::AutotuneKey;
16use super::AutotuneOperation;
17use super::AutotuneOperationSet;
18use alloc::boxed::Box;
19use hashbrown::HashMap;
20
21#[cfg(feature = "autotune-persistent-cache")]
22pub fn get_persistent_cache_file_path(prefix: &str) -> PathBuf {
25 let home_dir = dirs::home_dir().expect("An home directory should exist");
26 let path_dir = home_dir.join(".cache").join("burn").join("autotune");
27 let path = Path::new(&path_dir);
28 path.join(format!("{}-autotune-cache.json", prefix))
29}
30
31#[derive(Debug)]
33pub(crate) struct InMemoryCacheEntry {
34 #[cfg(feature = "autotune-persistent-cache")]
35 checksum_checked: bool,
36 fastest_index: usize,
37}
38
39#[cfg(feature = "autotune-persistent-cache")]
41#[derive(Debug, Serialize, Deserialize)]
42pub(crate) struct PersistentCacheEntry {
43 checksum: String,
44 fastest_index: usize,
45}
46
47#[derive(Debug)]
49pub(crate) struct TuneCache<K> {
50 in_memory_cache: HashMap<K, InMemoryCacheEntry>,
51 #[cfg(feature = "autotune-persistent-cache")]
52 persistent_cache: HashMap<K, PersistentCacheEntry>,
53 #[cfg(feature = "autotune-persistent-cache")]
54 device_id: String,
55}
56
57pub enum TuneCacheResult<K> {
59 Hit(Box<dyn AutotuneOperation>),
61 Miss(Box<dyn AutotuneOperationSet<K>>),
63}
64
65impl<K: AutotuneKey> TuneCache<K> {
66 pub(crate) fn new(
67 #[cfg_attr(not(feature = "autotune-persistent-cache"), allow(unused_variables))]
68 device_id: &str,
69 ) -> Self {
70 #[cfg(feature = "autotune-persistent-cache")]
71 {
72 let mut cache = TuneCache {
73 in_memory_cache: HashMap::new(),
74 persistent_cache: HashMap::new(),
75 device_id: device_id.to_string(),
76 };
77 if let Err(e) = cache.load() {
78 log::warn!(
79 "Unable to load autotune cache. Cache will be ignored ({}).",
80 e
81 );
82 }
83 cache
84 }
85
86 #[cfg(not(feature = "autotune-persistent-cache"))]
87 {
88 TuneCache {
89 in_memory_cache: HashMap::new(),
90 }
91 }
92 }
93
94 pub(crate) fn find_fastest(&self, key: &K) -> Option<usize> {
95 let result = self.in_memory_cache.get(key);
96
97 let val = match result {
98 Some(val) => val,
99 None => return None,
100 };
101
102 #[cfg(feature = "autotune-persistent-cache")]
103 if val.checksum_checked {
104 Some(val.fastest_index)
105 } else {
106 None
107 }
108
109 #[cfg(not(feature = "autotune-persistent-cache"))]
110 Some(val.fastest_index)
111 }
112
113 pub(crate) fn try_cache(
114 &mut self,
115 autotune_operation_set: Box<dyn AutotuneOperationSet<K>>,
116 ) -> TuneCacheResult<K> {
117 let key = autotune_operation_set.key();
118 let result = self.in_memory_cache.get_mut(&key);
119
120 #[cfg(feature = "autotune-persistent-cache")]
121 {
122 if let Some(InMemoryCacheEntry {
123 checksum_checked,
124 fastest_index,
125 }) = result
126 {
127 if !*checksum_checked {
128 let checksum = autotune_operation_set.compute_checksum();
129 let persistent_entry = self
130 .persistent_cache
131 .get(&key)
132 .expect("Both caches should be in sync");
133 if checksum != persistent_entry.checksum {
134 return TuneCacheResult::Miss(autotune_operation_set);
135 }
136 *checksum_checked = true;
137 }
138 return TuneCacheResult::Hit(autotune_operation_set.fastest(*fastest_index));
139 }
140 }
141
142 #[cfg(not(feature = "autotune-persistent-cache"))]
143 {
144 if let Some(InMemoryCacheEntry { fastest_index, .. }) = result {
145 return TuneCacheResult::Hit(autotune_operation_set.fastest(*fastest_index));
146 }
147 }
148
149 TuneCacheResult::Miss(autotune_operation_set)
150 }
151
152 pub(crate) fn cache_insert(&mut self, key: K, fastest_index: usize) {
153 self.in_memory_cache.insert(
154 key,
155 InMemoryCacheEntry {
156 #[cfg(feature = "autotune-persistent-cache")]
157 checksum_checked: true,
158 fastest_index,
159 },
160 );
161 }
162
163 #[cfg(feature = "autotune-persistent-cache")]
164 pub(crate) fn persistent_cache_insert(
165 &mut self,
166 key: K,
167 checksum: String,
168 fastest_index: usize,
169 ) {
170 self.persistent_cache.insert(
171 key,
172 PersistentCacheEntry {
173 checksum,
174 fastest_index,
175 },
176 );
177 }
178
179 #[cfg(feature = "autotune-persistent-cache")]
181 pub(crate) fn load(&mut self) -> Result<(), io::Error> {
182 let file_path = self.get_persistent_cache_file_path();
183 match fs::read_to_string(file_path) {
188 Ok(data) => {
189 let data: Vec<(K, PersistentCacheEntry)> = serde_json::from_str(&data)?;
190 for (key, value) in data.into_iter() {
191 self.persistent_cache.insert(key, value);
192 }
193 Ok(())
194 }
195 Err(e) => {
196 if e.kind() == std::io::ErrorKind::NotFound {
197 Ok(())
198 } else {
199 Err(e)
200 }
201 }
202 }?;
203 for (key, entry) in self.persistent_cache.iter() {
204 self.in_memory_cache.insert(
205 key.clone(),
206 InMemoryCacheEntry {
207 checksum_checked: false,
208 fastest_index: entry.fastest_index,
209 },
210 );
211 }
212 Ok(())
213 }
214
215 #[cfg(feature = "autotune-persistent-cache")]
217 pub(crate) fn save(&self) {
218 let file_path = self.get_persistent_cache_file_path();
219 if let Some(parent_dir) = file_path.parent() {
220 if !parent_dir.exists() {
221 fs::create_dir_all(parent_dir).unwrap_or_else(|_| {
222 panic!(
223 "Should be able to create directory '{}' for autotune persistent cache file",
224 parent_dir.to_str().unwrap())
225 });
226 }
227 }
228 let file = File::create(file_path.clone()).unwrap_or_else(|_| {
229 panic!(
230 "Should be able to open autotune persistent cache file '{}'",
231 file_path.to_str().unwrap()
232 )
233 });
234 let data = self.persistent_cache.iter().collect::<Vec<_>>();
235 serde_json::to_writer_pretty(file, &data)
236 .expect("Should be able to write to autotune persistent cache");
237 }
238
239 #[cfg(feature = "autotune-persistent-cache")]
241 pub fn get_persistent_cache_file_path(&self) -> PathBuf {
242 get_persistent_cache_file_path(&self.device_id)
243 }
244}