1#[cfg(not(any(feature = "cuda", feature = "rayon", feature = "cpu-fallback")))]
35compile_error!(
36 "flash-map: enable at least one of 'cuda', 'rayon', or 'cpu-fallback' features"
37);
38
39mod error;
40mod hash;
41
42#[cfg(feature = "cuda")]
43mod gpu;
44
45#[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
46mod cpu;
47
48#[cfg(feature = "rayon")]
49mod rayon_cpu;
50
51#[cfg(feature = "tokio")]
52mod async_map;
53
54pub use bytemuck::Pod;
55pub use error::FlashMapError;
56pub use hash::HashStrategy;
57
58#[cfg(feature = "tokio")]
59pub use async_map::AsyncFlashMap;
60
61use bytemuck::Pod as PodBound;
62
63pub struct FlashMap<K: PodBound, V: PodBound> {
78 inner: FlashMapBackend<K, V>,
79}
80
81enum FlashMapBackend<K: PodBound, V: PodBound> {
82 #[cfg(feature = "cuda")]
83 Gpu(gpu::GpuFlashMap<K, V>),
84 #[cfg(feature = "rayon")]
85 Rayon(rayon_cpu::RayonFlashMap<K, V>),
86 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
87 Cpu(cpu::CpuFlashMap<K, V>),
88}
89
90impl<K: PodBound + Send + Sync, V: PodBound + Send + Sync> FlashMap<K, V> {
91 pub fn with_capacity(capacity: usize) -> Result<Self, FlashMapError> {
96 FlashMapBuilder::new(capacity).build()
97 }
98
99 pub fn builder(capacity: usize) -> FlashMapBuilder {
101 FlashMapBuilder::new(capacity)
102 }
103
104 pub fn bulk_get(&self, keys: &[K]) -> Result<Vec<Option<V>>, FlashMapError> {
106 match &self.inner {
107 #[cfg(feature = "cuda")]
108 FlashMapBackend::Gpu(m) => m.bulk_get(keys),
109 #[cfg(feature = "rayon")]
110 FlashMapBackend::Rayon(m) => m.bulk_get(keys),
111 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
112 FlashMapBackend::Cpu(m) => m.bulk_get(keys),
113 }
114 }
115
116 pub fn bulk_insert(&mut self, pairs: &[(K, V)]) -> Result<usize, FlashMapError> {
127 match &mut self.inner {
128 #[cfg(feature = "cuda")]
129 FlashMapBackend::Gpu(m) => m.bulk_insert(pairs),
130 #[cfg(feature = "rayon")]
131 FlashMapBackend::Rayon(m) => m.bulk_insert(pairs),
132 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
133 FlashMapBackend::Cpu(m) => m.bulk_insert(pairs),
134 }
135 }
136
137 pub fn bulk_remove(&mut self, keys: &[K]) -> Result<usize, FlashMapError> {
141 match &mut self.inner {
142 #[cfg(feature = "cuda")]
143 FlashMapBackend::Gpu(m) => m.bulk_remove(keys),
144 #[cfg(feature = "rayon")]
145 FlashMapBackend::Rayon(m) => m.bulk_remove(keys),
146 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
147 FlashMapBackend::Cpu(m) => m.bulk_remove(keys),
148 }
149 }
150
151 pub fn len(&self) -> usize {
153 match &self.inner {
154 #[cfg(feature = "cuda")]
155 FlashMapBackend::Gpu(m) => m.len(),
156 #[cfg(feature = "rayon")]
157 FlashMapBackend::Rayon(m) => m.len(),
158 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
159 FlashMapBackend::Cpu(m) => m.len(),
160 }
161 }
162
163 pub fn is_empty(&self) -> bool {
165 self.len() == 0
166 }
167
168 pub fn capacity(&self) -> usize {
170 match &self.inner {
171 #[cfg(feature = "cuda")]
172 FlashMapBackend::Gpu(m) => m.capacity(),
173 #[cfg(feature = "rayon")]
174 FlashMapBackend::Rayon(m) => m.capacity(),
175 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
176 FlashMapBackend::Cpu(m) => m.capacity(),
177 }
178 }
179
180 pub fn load_factor(&self) -> f64 {
182 match &self.inner {
183 #[cfg(feature = "cuda")]
184 FlashMapBackend::Gpu(m) => m.load_factor(),
185 #[cfg(feature = "rayon")]
186 FlashMapBackend::Rayon(m) => m.load_factor(),
187 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
188 FlashMapBackend::Cpu(m) => m.load_factor(),
189 }
190 }
191
192 pub fn clear(&mut self) -> Result<(), FlashMapError> {
194 match &mut self.inner {
195 #[cfg(feature = "cuda")]
196 FlashMapBackend::Gpu(m) => m.clear(),
197 #[cfg(feature = "rayon")]
198 FlashMapBackend::Rayon(m) => m.clear(),
199 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
200 FlashMapBackend::Cpu(m) => m.clear(),
201 }
202 }
203}
204
205impl<K: PodBound + Send + Sync, V: PodBound + Send + Sync> std::fmt::Debug
206 for FlashMap<K, V>
207{
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 let backend = match &self.inner {
210 #[cfg(feature = "cuda")]
211 FlashMapBackend::Gpu(_) => "GPU",
212 #[cfg(feature = "rayon")]
213 FlashMapBackend::Rayon(_) => "Rayon",
214 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
215 FlashMapBackend::Cpu(_) => "CPU",
216 };
217 f.debug_struct("FlashMap")
218 .field("backend", &backend)
219 .field("len", &self.len())
220 .field("capacity", &self.capacity())
221 .field("load_factor", &format!("{:.1}%", self.load_factor() * 100.0))
222 .finish()
223 }
224}
225
226pub struct FlashMapBuilder {
232 capacity: usize,
233 hash_strategy: HashStrategy,
234 device_id: usize,
235 force_cpu: bool,
236}
237
238impl FlashMapBuilder {
239 pub fn new(capacity: usize) -> Self {
241 Self {
242 capacity,
243 hash_strategy: HashStrategy::Identity,
244 device_id: 0,
245 force_cpu: false,
246 }
247 }
248
249 pub fn hash_strategy(mut self, strategy: HashStrategy) -> Self {
251 self.hash_strategy = strategy;
252 self
253 }
254
255 pub fn device_id(mut self, id: usize) -> Self {
257 self.device_id = id;
258 self
259 }
260
261 pub fn force_cpu(mut self) -> Self {
263 self.force_cpu = true;
264 self
265 }
266
267 pub fn build<K: PodBound + Send + Sync, V: PodBound + Send + Sync>(
269 self,
270 ) -> Result<FlashMap<K, V>, FlashMapError> {
271 let mut _gpu_err: Option<FlashMapError> = None;
272
273 #[cfg(feature = "cuda")]
274 if !self.force_cpu {
275 match gpu::GpuFlashMap::<K, V>::new(
276 self.capacity,
277 self.hash_strategy,
278 self.device_id,
279 ) {
280 Ok(m) => return Ok(FlashMap { inner: FlashMapBackend::Gpu(m) }),
281 Err(e) => _gpu_err = Some(e),
282 }
283 }
284
285 #[cfg(feature = "rayon")]
286 {
287 if let Some(ref e) = _gpu_err {
288 eprintln!("[flash-map] GPU unavailable ({e}), using Rayon backend");
289 }
290 return Ok(FlashMap {
291 inner: FlashMapBackend::Rayon(rayon_cpu::RayonFlashMap::new(
292 self.capacity,
293 self.hash_strategy,
294 )),
295 });
296 }
297
298 #[cfg(all(feature = "cpu-fallback", not(feature = "rayon")))]
299 {
300 if let Some(ref e) = _gpu_err {
301 eprintln!("[flash-map] GPU unavailable ({e}), using CPU fallback");
302 }
303 return Ok(FlashMap {
304 inner: FlashMapBackend::Cpu(cpu::CpuFlashMap::new(
305 self.capacity,
306 self.hash_strategy,
307 )),
308 });
309 }
310
311 #[allow(unreachable_code)]
312 match _gpu_err {
313 Some(e) => Err(e),
314 None => Err(FlashMapError::NoBackend),
315 }
316 }
317}