linkrs_storage/edge/mutable_csr/
live_set.rs1use std::collections::HashMap;
2
3use super::super::csr_shared::SegmentedTable;
4use super::write::EdgePosition;
5use super::MutableCsr;
6
7pub(crate) const LIVE_SET_WIDTH_BOUND: usize = 8;
17
18#[derive(Debug, Clone)]
40pub(crate) enum LiveKeySet {
41 Hash(HashMap<(u32, i64), EdgePosition>),
42 Sorted(Vec<((u32, i64), EdgePosition)>),
43}
44
45impl Default for LiveKeySet {
46 fn default() -> Self {
47 Self::Hash(HashMap::new())
48 }
49}
50
51impl LiveKeySet {
52 pub(crate) fn from_positions(positions: Vec<((u32, i64), EdgePosition)>) -> Self {
53 let mut sorted = positions;
54 sorted.sort_unstable_by_key(|(k, _)| *k);
55 Self::Sorted(sorted)
56 }
57
58 pub(crate) fn contains(&self, key: &(u32, i64)) -> bool {
59 match self {
60 Self::Hash(map) => map.contains_key(key),
61 Self::Sorted(vec) => {
62 let idx = vec.partition_point(|(k, _)| k < key);
63 idx < vec.len() && vec[idx].0 == *key
64 }
65 }
66 }
67
68 pub(crate) fn position(&self, key: &(u32, i64)) -> Option<EdgePosition> {
69 match self {
70 Self::Hash(map) => map.get(key).copied(),
71 Self::Sorted(vec) => {
72 let idx = vec.partition_point(|(k, _)| k < key);
73 if idx < vec.len() && vec[idx].0 == *key {
74 Some(vec[idx].1)
75 } else {
76 None
77 }
78 }
79 }
80 }
81
82 pub(crate) fn len(&self) -> usize {
83 match self {
84 Self::Hash(map) => map.len(),
85 Self::Sorted(vec) => vec.len(),
86 }
87 }
88
89 pub(crate) fn is_empty(&self) -> bool {
90 match self {
91 Self::Hash(map) => map.is_empty(),
92 Self::Sorted(vec) => vec.is_empty(),
93 }
94 }
95
96 pub(crate) fn insert(&mut self, key: (u32, i64), position: EdgePosition) {
101 match self {
102 Self::Hash(map) => {
103 map.insert(key, position);
104 }
105 Self::Sorted(_) => {
106 let sorted = std::mem::take(self).into_sorted();
107 let mut map = HashMap::with_capacity(sorted.len() + 1);
108 map.extend(sorted);
109 map.insert(key, position);
110 *self = Self::Hash(map);
111 }
112 }
113 }
114
115 pub(crate) fn remove(&mut self, key: &(u32, i64)) {
122 match self {
123 Self::Hash(map) => {
124 map.remove(key);
125 }
126 Self::Sorted(_) => {
127 let sorted = std::mem::take(self).into_sorted();
128 let mut map = HashMap::with_capacity(sorted.len());
129 map.extend(sorted);
130 map.remove(key);
131 *self = Self::Hash(map);
132 }
133 }
134 }
135
136 pub(crate) fn heap_bytes(&self) -> usize {
138 let entry_size = std::mem::size_of::<((u32, i64), EdgePosition)>() + 8;
139 match self {
140 Self::Hash(map) => map.len() * entry_size,
141 Self::Sorted(vec) => vec.len() * entry_size,
142 }
143 }
144
145 fn into_sorted(self) -> Vec<((u32, i64), EdgePosition)> {
148 match self {
149 Self::Hash(map) => {
150 let mut entries: Vec<_> = map.into_iter().collect();
151 entries.sort_unstable_by_key(|(k, _)| *k);
152 entries
153 }
154 Self::Sorted(vec) => vec,
155 }
156 }
157}
158
159#[derive(Debug, Clone, Default)]
168pub(crate) struct LiveSetStorage {
169 table: SegmentedTable<LiveKeySet>,
170 live_rows: usize,
171}
172
173impl LiveSetStorage {
174 pub(crate) fn new() -> Self {
175 Self {
176 table: SegmentedTable::new(),
177 live_rows: 0,
178 }
179 }
180
181 pub(crate) fn ensure_capacity(&mut self, vertex_capacity: usize) {
182 self.table.ensure_capacity(vertex_capacity);
183 }
184
185 pub(crate) fn get(&self, vid: &u32) -> Option<&LiveKeySet> {
186 self.table.get(*vid)
187 }
188
189 pub(crate) fn insert(&mut self, vid: u32, set: LiveKeySet) {
190 let slot = self.table.slot_mut(vid);
191 if slot.is_none() {
192 self.live_rows += 1;
193 }
194 *slot = Some(set);
195 }
196
197 pub(crate) fn remove(&mut self, vid: &u32) {
198 if self.table.take(*vid).is_some() {
199 self.live_rows = self.live_rows.saturating_sub(1);
200 }
201 }
202
203 pub(crate) fn insert_key(&mut self, vid: u32, key: (u32, i64), position: EdgePosition) {
204 let slot = self.table.slot_mut(vid);
205 match slot {
206 Some(set) => set.insert(key, position),
207 slot @ None => {
208 *slot = Some(LiveKeySet::from_positions(vec![(key, position)]));
209 self.live_rows += 1;
210 }
211 }
212 }
213
214 pub(crate) fn remove_key(&mut self, vid: u32, key: &(u32, i64)) {
215 let emptied = match self.table.get_mut(vid) {
216 Some(set) => {
217 set.remove(key);
218 set.is_empty()
219 }
220 None => return,
221 };
222 if emptied {
223 self.table.take(vid);
224 self.live_rows = self.live_rows.saturating_sub(1);
225 }
226 }
227
228 pub(crate) fn clear(&mut self) {
229 self.table.clear();
230 self.live_rows = 0;
231 }
232
233 pub(crate) fn heap_bytes_total(&self) -> usize {
234 self.table.iter().map(|(_, set)| set.heap_bytes()).sum()
235 }
236
237 pub(crate) fn index_bytes(&self) -> usize {
238 self.table.table_bytes()
239 + self.live_rows * (std::mem::size_of::<u32>() + std::mem::size_of::<LiveKeySet>())
240 }
241}
242
243impl MutableCsr {
244 pub fn has_live_set(&self, vid: &u32) -> bool {
245 self.live_sets.get(vid).is_some()
246 }
247
248 pub(crate) fn layer_counts(&self) -> (usize, usize) {
255 let mut narrow = 0usize;
256 let mut wide = 0usize;
257 for vid in 0..self.vertex_capacity() as u32 {
258 if self.live_sets.get(&vid).is_some() {
259 wide += 1;
260 } else if self.row_live_count(vid) > 0 {
261 narrow += 1;
262 }
263 }
264 (narrow, wide)
265 }
266}
267
268impl MutableCsr {
269 pub(crate) fn rebuild_live_sets(&mut self) {
270 let capacity = self.vertex_capacity() as u32;
271 self.live_sets.clear();
272 self.live_sets.ensure_capacity(self.vertex_capacity());
273 for vid in 0..capacity {
274 self.rebuild_live_set_for_vertex(vid);
275 }
276 }
277 pub(crate) fn live_key_count(&self, vid: u32) -> usize {
283 if let Some(set) = self.live_sets.get(&vid) {
284 return set.len();
285 }
286 self.row_live_count(vid)
287 }
288
289 pub(crate) fn row_live_scan(&self, vid: u32, endpoint: u32, rank: i64) -> (bool, usize) {
300 let idx = vid as usize;
301 if idx >= self.vertex_capacity() {
302 return (false, 0);
303 }
304 let mut present = false;
305 let (hot, cold) = self.primary_pair(idx);
306 for (h, c) in hot.iter().zip(cold.iter()) {
307 if c.is_live() && h.endpoint == endpoint && h.rank == rank {
308 present = true;
309 break;
310 }
311 }
312 if !present {
313 if let Some(chunks) = self.overflow_chunks.get(vid) {
314 'outer: for chunk in chunks {
315 for (hot, cold) in chunk.hot_slice().iter().zip(chunk.cold_slice()) {
316 if cold.is_live() && hot.endpoint == endpoint && hot.rank == rank {
317 present = true;
318 break 'outer;
319 }
320 }
321 }
322 }
323 }
324 let live = self.live_counts[idx] as usize;
325 (present, live)
326 }
327
328 pub(crate) fn row_live_count(&self, vid: u32) -> usize {
332 let idx = vid as usize;
333 if idx >= self.vertex_capacity() {
334 return 0;
335 }
336 self.live_counts[idx] as usize
337 }
338
339 pub(crate) fn track_live_insert(
340 &mut self,
341 vid: u32,
342 endpoint: u32,
343 rank: i64,
344 position: EdgePosition,
345 ) {
346 if self.live_sets.get(&vid).is_some() {
347 self.live_sets.insert_key(vid, (endpoint, rank), position);
348 return;
349 }
350 let mut width = 0usize;
358 if (vid as usize) < self.vertex_capacity() {
359 width = self.rows.degrees[vid as usize] as usize;
360 if let Some(chunks) = self.overflow_chunks.get(vid) {
361 width += chunks.iter().map(|chunk| chunk.len()).sum::<usize>();
362 }
363 }
364 if width > LIVE_SET_WIDTH_BOUND {
365 self.rebuild_live_set_for_vertex(vid);
366 }
367 }
368
369 pub(crate) fn track_live_remove(&mut self, vid: u32, endpoint: u32, rank: i64) {
370 if self.live_sets.get(&vid).is_none() {
372 return;
373 }
374 self.live_sets.remove_key(vid, &(endpoint, rank));
375 }
376
377 pub(crate) fn rebuild_live_set_for_vertex(&mut self, vid: u32) {
378 let idx = vid as usize;
379 if idx >= self.vertex_capacity() {
380 self.live_sets.remove(&vid);
381 self.live_set_rebuild_count += 1;
382 return;
383 }
384 let (hot, cold) = self.primary_pair(idx);
385 let mut positioned = Vec::new();
386 let mut live = 0u32;
387 let mut tombstones = 0u32;
388 for (slot, (h, c)) in hot.iter().zip(cold.iter()).enumerate() {
389 if c.is_live() {
390 live += 1;
391 positioned.push((
392 (h.endpoint, h.rank),
393 EdgePosition::Primary { slot: slot as u32 },
394 ));
395 } else {
396 tombstones += 1;
397 }
398 }
399 if let Some(chunks) = self.overflow_chunks.get(vid) {
400 for (chunk_idx, chunk) in chunks.iter().enumerate() {
401 for (slot_idx, (hot, cold)) in
402 chunk.hot_slice().iter().zip(chunk.cold_slice()).enumerate()
403 {
404 if cold.is_live() {
405 live += 1;
406 positioned.push((
407 (hot.endpoint, hot.rank),
408 EdgePosition::Overflow {
409 chunk: chunk_idx as u32,
410 slot: slot_idx as u32,
411 },
412 ));
413 } else {
414 tombstones += 1;
415 }
416 }
417 }
418 }
419 self.live_counts[idx] = live;
420 self.tombstone_counts[idx] = tombstones;
421 if positioned.len() <= LIVE_SET_WIDTH_BOUND {
423 self.live_sets.remove(&vid);
424 } else {
425 self.live_sets
426 .insert(vid, LiveKeySet::from_positions(positioned));
427 }
428 self.live_set_rebuild_count += 1;
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435
436 #[test]
437 fn set_contains_and_removes() {
438 use super::super::write::EdgePosition;
439 let mut set = LiveKeySet::from_positions(vec![
440 ((3, 0), EdgePosition::Primary { slot: 0 }),
441 ((1, 0), EdgePosition::Primary { slot: 1 }),
442 ((2, 0), EdgePosition::Primary { slot: 2 }),
443 ]);
444 assert!(set.contains(&(1, 0)));
445 assert_eq!(
446 set.position(&(1, 0)),
447 Some(EdgePosition::Primary { slot: 1 })
448 );
449 assert!(!set.contains(&(9, 0)));
450 assert_eq!(set.position(&(9, 0)), None);
451 set.remove(&(2, 0));
452 assert!(!set.contains(&(2, 0)));
453 assert_eq!(set.len(), 2);
454 }
455
456 #[test]
457 fn wide_rows_carry_a_set() {
458 use super::super::write::EdgePosition;
459 let mut set = LiveKeySet::default();
460 for i in 0..=(LIVE_SET_WIDTH_BOUND as u32) {
461 set.insert((i, 0), EdgePosition::Primary { slot: i });
462 }
463 for i in 0..=(LIVE_SET_WIDTH_BOUND as u32) {
464 assert!(set.contains(&(i, 0)));
465 assert_eq!(
466 set.position(&(i, 0)),
467 Some(EdgePosition::Primary { slot: i })
468 );
469 }
470 set.remove(&(0, 0));
471 assert!(!set.contains(&(0, 0)));
472 }
473
474 #[test]
475 fn duplicate_inserts_stay_unique() {
476 use super::super::write::EdgePosition;
477 let mut set = LiveKeySet::default();
478 set.insert((1, 5), EdgePosition::Primary { slot: 0 });
479 set.insert((1, 5), EdgePosition::Primary { slot: 0 });
480 assert_eq!(set.len(), 1);
481 }
482
483 #[test]
484 fn empty_vertices_cost_only_pointers() {
485 use super::super::super::csr_shared::SEGMENT_SIZE;
486 let mut storage = LiveSetStorage::new();
487 storage.ensure_capacity(1_000_000);
488 assert!(storage.index_bytes() < 1_000_000);
489 assert!(storage.get(&999_999).is_none());
490 storage.insert(
493 999_999,
494 LiveKeySet::from_positions(vec![(
495 (7, 0),
496 super::super::write::EdgePosition::Primary { slot: 0 },
497 )]),
498 );
499 assert!(storage.get(&999_999).is_some());
500 let _ = SEGMENT_SIZE;
501 }
502
503 #[test]
504 fn sparse_rows_route_and_iterate_in_order() {
505 use super::super::super::csr_shared::SEGMENT_SIZE;
506 use super::super::write::EdgePosition;
507 let mut storage = LiveSetStorage::new();
508 let seg = SEGMENT_SIZE as u32;
509 storage.insert_key(3 * seg + 7, (9, 0), EdgePosition::Primary { slot: 0 });
510 storage.insert_key(5, (1, 0), EdgePosition::Primary { slot: 0 });
511 storage.insert_key(seg, (2, 0), EdgePosition::Primary { slot: 0 });
512 assert!(storage.get(&seg).is_some_and(|set| set.contains(&(2, 0))));
513 assert!(storage.get(&(seg - 1)).is_none());
514 storage.remove_key(seg, &(2, 0));
515 assert!(storage.get(&seg).is_none());
516 storage.insert_key(seg, (4, 0), EdgePosition::Primary { slot: 3 });
517 assert!(storage.get(&seg).is_some_and(|set| set.contains(&(4, 0))));
518 }
519}