1#[cfg(not(any(feature = "cuda", feature = "cpu-fallback")))]
35compile_error!("flash-map: enable at least one of 'cuda' or 'cpu-fallback' features");
36
37mod error;
38mod hash;
39
40#[cfg(feature = "cuda")]
41mod gpu;
42
43#[cfg(feature = "cpu-fallback")]
44mod cpu;
45
46pub use bytemuck::Pod;
47pub use error::FlashMapError;
48pub use hash::HashStrategy;
49
50use bytemuck::Pod as PodBound;
51
52pub struct FlashMap<K: PodBound, V: PodBound> {
67 inner: FlashMapBackend<K, V>,
68}
69
70enum FlashMapBackend<K: PodBound, V: PodBound> {
71 #[cfg(feature = "cuda")]
72 Gpu(gpu::GpuFlashMap<K, V>),
73 #[cfg(feature = "cpu-fallback")]
74 Cpu(cpu::CpuFlashMap<K, V>),
75}
76
77impl<K: PodBound, V: PodBound> FlashMap<K, V> {
78 pub fn with_capacity(capacity: usize) -> Result<Self, FlashMapError> {
83 FlashMapBuilder::new(capacity).build()
84 }
85
86 pub fn builder(capacity: usize) -> FlashMapBuilder {
88 FlashMapBuilder::new(capacity)
89 }
90
91 pub fn bulk_get(&self, keys: &[K]) -> Result<Vec<Option<V>>, FlashMapError> {
93 match &self.inner {
94 #[cfg(feature = "cuda")]
95 FlashMapBackend::Gpu(m) => m.bulk_get(keys),
96 #[cfg(feature = "cpu-fallback")]
97 FlashMapBackend::Cpu(m) => m.bulk_get(keys),
98 }
99 }
100
101 pub fn bulk_insert(&mut self, pairs: &[(K, V)]) -> Result<usize, FlashMapError> {
112 match &mut self.inner {
113 #[cfg(feature = "cuda")]
114 FlashMapBackend::Gpu(m) => m.bulk_insert(pairs),
115 #[cfg(feature = "cpu-fallback")]
116 FlashMapBackend::Cpu(m) => m.bulk_insert(pairs),
117 }
118 }
119
120 pub fn bulk_remove(&mut self, keys: &[K]) -> Result<usize, FlashMapError> {
124 match &mut self.inner {
125 #[cfg(feature = "cuda")]
126 FlashMapBackend::Gpu(m) => m.bulk_remove(keys),
127 #[cfg(feature = "cpu-fallback")]
128 FlashMapBackend::Cpu(m) => m.bulk_remove(keys),
129 }
130 }
131
132 pub fn len(&self) -> usize {
134 match &self.inner {
135 #[cfg(feature = "cuda")]
136 FlashMapBackend::Gpu(m) => m.len(),
137 #[cfg(feature = "cpu-fallback")]
138 FlashMapBackend::Cpu(m) => m.len(),
139 }
140 }
141
142 pub fn is_empty(&self) -> bool {
144 self.len() == 0
145 }
146
147 pub fn capacity(&self) -> usize {
149 match &self.inner {
150 #[cfg(feature = "cuda")]
151 FlashMapBackend::Gpu(m) => m.capacity(),
152 #[cfg(feature = "cpu-fallback")]
153 FlashMapBackend::Cpu(m) => m.capacity(),
154 }
155 }
156
157 pub fn load_factor(&self) -> f64 {
159 match &self.inner {
160 #[cfg(feature = "cuda")]
161 FlashMapBackend::Gpu(m) => m.load_factor(),
162 #[cfg(feature = "cpu-fallback")]
163 FlashMapBackend::Cpu(m) => m.load_factor(),
164 }
165 }
166
167 pub fn clear(&mut self) -> Result<(), FlashMapError> {
169 match &mut self.inner {
170 #[cfg(feature = "cuda")]
171 FlashMapBackend::Gpu(m) => m.clear(),
172 #[cfg(feature = "cpu-fallback")]
173 FlashMapBackend::Cpu(m) => m.clear(),
174 }
175 }
176}
177
178impl<K: PodBound, V: PodBound> std::fmt::Debug for FlashMap<K, V> {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 let backend = match &self.inner {
181 #[cfg(feature = "cuda")]
182 FlashMapBackend::Gpu(_) => "GPU",
183 #[cfg(feature = "cpu-fallback")]
184 FlashMapBackend::Cpu(_) => "CPU",
185 };
186 f.debug_struct("FlashMap")
187 .field("backend", &backend)
188 .field("len", &self.len())
189 .field("capacity", &self.capacity())
190 .field("load_factor", &format!("{:.1}%", self.load_factor() * 100.0))
191 .finish()
192 }
193}
194
195pub struct FlashMapBuilder {
201 capacity: usize,
202 hash_strategy: HashStrategy,
203 device_id: usize,
204 force_cpu: bool,
205}
206
207impl FlashMapBuilder {
208 pub fn new(capacity: usize) -> Self {
210 Self {
211 capacity,
212 hash_strategy: HashStrategy::Identity,
213 device_id: 0,
214 force_cpu: false,
215 }
216 }
217
218 pub fn hash_strategy(mut self, strategy: HashStrategy) -> Self {
220 self.hash_strategy = strategy;
221 self
222 }
223
224 pub fn device_id(mut self, id: usize) -> Self {
226 self.device_id = id;
227 self
228 }
229
230 pub fn force_cpu(mut self) -> Self {
232 self.force_cpu = true;
233 self
234 }
235
236 pub fn build<K: PodBound, V: PodBound>(self) -> Result<FlashMap<K, V>, FlashMapError> {
238 let mut _gpu_err: Option<FlashMapError> = None;
239
240 #[cfg(feature = "cuda")]
241 if !self.force_cpu {
242 match gpu::GpuFlashMap::<K, V>::new(
243 self.capacity,
244 self.hash_strategy,
245 self.device_id,
246 ) {
247 Ok(m) => return Ok(FlashMap { inner: FlashMapBackend::Gpu(m) }),
248 Err(e) => _gpu_err = Some(e),
249 }
250 }
251
252 #[cfg(feature = "cpu-fallback")]
253 {
254 if let Some(ref e) = _gpu_err {
255 eprintln!("[flash-map] GPU unavailable ({e}), using CPU fallback");
256 }
257 return Ok(FlashMap {
258 inner: FlashMapBackend::Cpu(cpu::CpuFlashMap::new(
259 self.capacity,
260 self.hash_strategy,
261 )),
262 });
263 }
264
265 #[allow(unreachable_code)]
266 match _gpu_err {
267 Some(e) => Err(e),
268 None => Err(FlashMapError::NoBackend),
269 }
270 }
271}