linkrs_storage/edge/node_group/
stats.rs1use linkrs_core::types::EdgeStrategy;
5
6use super::super::{CsrBase, FragmentationStats, MutableCsrTrait};
7use super::{group_base, CsrShardSet};
8
9#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct NodeGroupStats {
12 pub group: usize,
13 pub base: u32,
14 pub rows: usize,
15 pub live_edges: u64,
16 pub capacity: usize,
17 pub density: f32,
18 pub dirty: super::GroupDirty,
19}
20
21impl CsrShardSet {
22 pub fn group_stats(&self, gid: usize) -> Option<NodeGroupStats> {
24 let shard = self.shards.get(&gid)?;
25 let live = shard.variant.edge_count();
26 let capacity = shard
27 .variant
28 .fragmentation_stats()
29 .map(|stats| stats.total_capacity)
30 .unwrap_or_else(|| shard.variant.vertex_capacity());
31 let density = if capacity == 0 {
32 1.0
33 } else {
34 live as f32 / capacity as f32
35 };
36 Some(NodeGroupStats {
37 group: gid,
38 base: group_base(gid, self.group_bits),
39 rows: self.group_size(),
40 live_edges: live,
41 capacity,
42 density,
43 dirty: shard.dirty,
44 })
45 }
46
47 pub fn group_live_count(&self, gid: usize) -> u64 {
49 self.shards
50 .get(&gid)
51 .map(|shard| shard.variant.edge_count())
52 .unwrap_or(0)
53 }
54
55 pub fn bytes_per_edge(&self) -> usize {
60 FragmentationStats::measured_bytes_per_edge(self.used_memory_size(), self.edge_count())
61 }
62
63 pub fn fragmentation_stats(&self) -> Option<FragmentationStats> {
68 let mut total_capacity = 0usize;
69 let mut reachable_edges = 0usize;
70 let mut dead_entries = 0usize;
71 let mut wasted_capacity = 0usize;
72 let mut reported = false;
73 for shard in self.shards.values() {
74 if let Some(stats) = shard.variant.fragmentation_stats() {
75 reported = true;
76 total_capacity += stats.total_capacity;
77 reachable_edges += stats.reachable_edges;
78 dead_entries += stats.dead_entries;
79 wasted_capacity += stats.wasted_capacity;
80 }
81 }
82 if !reported {
83 return None;
84 }
85 Some(FragmentationStats::with_dead_info(
86 total_capacity,
87 reachable_edges,
88 dead_entries,
89 wasted_capacity,
90 ))
91 }
92
93 pub fn fragmentation_ratio(&self) -> f32 {
98 self.fragmentation_stats()
99 .map(|stats| stats.fragmentation_ratio())
100 .unwrap_or(0.0)
101 }
102
103 pub fn wasted_bytes_estimate(&self) -> usize {
105 self.shards
106 .values()
107 .map(|shard| shard.variant.wasted_bytes_estimate())
108 .sum()
109 }
110
111 pub fn used_memory_size(&self) -> usize {
116 if self.strategy == EdgeStrategy::None {
117 return std::mem::size_of::<Self>();
118 }
119 self.shards
120 .values()
121 .map(|shard| {
122 shard.variant.used_memory_size()
123 + shard.mapped.as_ref().map_or(0, |m| m.used_memory_size())
124 })
125 .sum::<usize>()
126 + std::mem::size_of::<Self>()
127 }
128
129 pub fn layer_counts(&self) -> (usize, usize) {
135 let mut narrow = 0usize;
136 let mut wide = 0usize;
137 for shard in self.shards.values() {
138 if let super::super::CsrVariant::Multiple(csr) = &shard.variant {
139 let (n, w) = csr.layer_counts();
140 narrow += n;
141 wide += w;
142 }
143 }
144 (narrow, wide)
145 }
146}