Use non-zero edge indices to reduce size of structs with Option fields
This commit is contained in:
+18
-7
@@ -1,5 +1,6 @@
|
||||
//! [`Graph`], an undirected graph topology supporting addition and deletion.
|
||||
|
||||
use std::num::NonZeroUsize;
|
||||
use typed_generational_arena::{Arena, Index};
|
||||
|
||||
use crate::maps::ElementMap;
|
||||
@@ -31,10 +32,20 @@ pub type GraphIncidence = Incidence<Vertex, Edge>;
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct VertexSlot(usize);
|
||||
|
||||
// TODO: Benchmark the cost of the index shift. An alternative could be to init Graph::incidences with two dummy entries to avoid 0.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct IncidenceSlot(usize);
|
||||
struct IncidenceSlot(NonZeroUsize);
|
||||
|
||||
impl IncidenceSlot {
|
||||
fn new(index: usize) -> Self {
|
||||
IncidenceSlot(NonZeroUsize::new(index + 1).unwrap())
|
||||
}
|
||||
|
||||
fn index(self) -> usize {
|
||||
self.0.get() - 1
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Check if VertexIncidenceHeader and IncidenceEntry can be made smaller. Currently they both take 24 bytes (on 64bit), see https://stackoverflow.com/a/79653173
|
||||
/// `pub` because [`Vertex`] references it as a type parameter of the underlying arena. Not
|
||||
/// intended for direct external use.
|
||||
#[doc(hidden)]
|
||||
@@ -147,7 +158,7 @@ impl Graph {
|
||||
adjacent: VertexSlot(v2.arr_idx()),
|
||||
});
|
||||
self.vertices[v1].incidence_count += 1;
|
||||
self.vertices[v1].first_incidence = Some(IncidenceSlot(edge.arr_idx()));
|
||||
self.vertices[v1].first_incidence = Some(IncidenceSlot::new(edge.arr_idx()));
|
||||
edge
|
||||
}
|
||||
|
||||
@@ -186,7 +197,7 @@ impl Graph {
|
||||
let first = vertex_header
|
||||
.first_incidence
|
||||
.expect("incident vertex without incidences, corrupt internal data state");
|
||||
if first.0 == e.arr_idx() {
|
||||
if first.index() == e.arr_idx() {
|
||||
vertex_header.first_incidence = next;
|
||||
} else {
|
||||
let graph: &Graph = self;
|
||||
@@ -195,7 +206,7 @@ impl Graph {
|
||||
.find(|(_, f)| {
|
||||
graph.incidences[*f]
|
||||
.next
|
||||
.is_some_and(|i| i.0 == e.arr_idx())
|
||||
.is_some_and(|i| i.index() == e.arr_idx())
|
||||
})
|
||||
.expect("cannot find previous incidence, corrupt internal data state");
|
||||
self.incidences[previous].next = next;
|
||||
@@ -210,7 +221,7 @@ impl Graph {
|
||||
fn step_incidence(&self, incidence: &mut Option<IncidenceSlot>) -> Option<(VertexSlot, Edge)> {
|
||||
// TODO: Benchmark storing full Index (one read, larger entries) vs. slot + get_idx() (two reads, smaller entries).
|
||||
let current = (*incidence)?;
|
||||
let e = self.incidences.get_idx(current.0).unwrap();
|
||||
let e = self.incidences.get_idx(current.index()).unwrap();
|
||||
let entry = self.incidences[e];
|
||||
*incidence = entry.next;
|
||||
Some((entry.adjacent, e))
|
||||
@@ -371,7 +382,7 @@ impl GraphTopologyDeletion for Graph {
|
||||
if e_entry.adjacent != f_entry.adjacent {
|
||||
self.update_incidence_list(e, f_entry.adjacent, e_entry.next, false);
|
||||
self.update_incidence_list(f, e_entry.adjacent, f_entry.next, false);
|
||||
} else if f_entry.next.is_some_and(|i| e.arr_idx() == i.0) {
|
||||
} else if f_entry.next.is_some_and(|i| e.arr_idx() == i.index()) {
|
||||
self.update_incidence_list(f, e_entry.adjacent, e_entry.next, true);
|
||||
} else {
|
||||
self.update_incidence_list(e, e_entry.adjacent, f_entry.next, true);
|
||||
|
||||
Reference in New Issue
Block a user