Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit f96de02

Browse files
author
Hendrik van Antwerpen
committed
Reduce path finding memory usage
Cycle detection was maintaining a list where the elements were either `PartialPath`s or `Handle`s. The former are much larger than the latter, resulting in a larger enum size, even though most values in the list would be the small ones. To solve this, an additional arena is introduced that holds the partial paths values themselves. The list elements are now handles into that arena.
1 parent 496392b commit f96de02

File tree

1 file changed

+44
-25
lines changed

1 file changed

+44
-25
lines changed

stack-graphs/src/cycles.rs

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use enumset::EnumSet;
3333
use smallvec::SmallVec;
3434
use std::collections::HashMap;
3535

36+
use crate::arena::Arena;
3637
use crate::arena::Handle;
3738
use crate::arena::List;
3839
use crate::arena::ListArena;
@@ -128,21 +129,29 @@ where
128129
// ----------------------------------------------------------------------------
129130
// Cycle detector
130131

131-
pub struct Appendables<H>(pub(crate) ListArena<PathOrAppendable<H>>);
132+
pub struct Appendables<H> {
133+
/// List arena for appendable lists
134+
elements: ListArena<InternedPathOrHandle<H>>,
135+
/// Arena for interned partial paths
136+
interned: Arena<PartialPath>,
137+
}
132138

133139
impl<H> Appendables<H> {
134140
pub fn new() -> Self {
135-
Self(ListArena::new())
141+
Self {
142+
elements: ListArena::new(),
143+
interned: Arena::new(),
144+
}
136145
}
137146
}
138147

139148
#[derive(Clone)]
140-
pub(crate) enum PathOrAppendable<H> {
141-
Path(PartialPath),
142-
Handle(H),
149+
pub(crate) enum InternedPathOrHandle<H> {
150+
Owned(Handle<PartialPath>),
151+
Database(H),
143152
}
144153

145-
impl<H> PathOrAppendable<H>
154+
impl<H> InternedPathOrHandle<H>
146155
where
147156
H: Clone,
148157
{
@@ -151,44 +160,45 @@ where
151160
graph: &StackGraph,
152161
partials: &mut PartialPaths,
153162
db: &'a Db,
163+
interned: &Arena<PartialPath>,
154164
path: &mut PartialPath,
155165
) -> Result<(), PathResolutionError>
156166
where
157167
A: Appendable + 'a,
158168
Db: ToAppendable<H, A>,
159169
{
160170
match self {
161-
Self::Path(other) => other.append_to(graph, partials, path),
162-
Self::Handle(h) => db.get_appendable(h).append_to(graph, partials, path),
171+
Self::Owned(h) => interned.get(*h).append_to(graph, partials, path),
172+
Self::Database(h) => db.get_appendable(h).append_to(graph, partials, path),
163173
}
164174
}
165175

166-
fn start_node<'a, A, Db>(&self, db: &'a Db) -> Handle<Node>
176+
fn start_node<'a, A, Db>(&self, db: &'a Db, interned: &Arena<PartialPath>) -> Handle<Node>
167177
where
168178
A: Appendable + 'a,
169179
Db: ToAppendable<H, A>,
170180
{
171181
match self {
172-
Self::Path(path) => path.start_node,
173-
Self::Handle(h) => db.get_appendable(h).start_node(),
182+
Self::Owned(h) => interned.get(*h).start_node,
183+
Self::Database(h) => db.get_appendable(h).start_node(),
174184
}
175185
}
176186

177-
fn end_node<'a, A, Db>(&self, db: &'a Db) -> Handle<Node>
187+
fn end_node<'a, A, Db>(&self, db: &'a Db, interned: &Arena<PartialPath>) -> Handle<Node>
178188
where
179189
A: Appendable + 'a,
180190
Db: ToAppendable<H, A>,
181191
{
182192
match self {
183-
Self::Path(path) => path.end_node,
184-
Self::Handle(h) => db.get_appendable(h).end_node(),
193+
Self::Owned(h) => interned.get(*h).end_node,
194+
Self::Database(h) => db.get_appendable(h).end_node(),
185195
}
186196
}
187197
}
188198

189199
#[derive(Clone)]
190200
pub struct AppendingCycleDetector<H> {
191-
appendages: List<PathOrAppendable<H>>,
201+
appendages: List<InternedPathOrHandle<H>>,
192202
}
193203

194204
impl<H> AppendingCycleDetector<H> {
@@ -199,16 +209,19 @@ impl<H> AppendingCycleDetector<H> {
199209
}
200210

201211
pub fn from(appendables: &mut Appendables<H>, path: PartialPath) -> Self {
212+
let h = appendables.interned.add(path);
202213
let mut result = Self::new();
203214
result
204215
.appendages
205-
.push_front(&mut appendables.0, PathOrAppendable::Path(path));
216+
.push_front(&mut appendables.elements, InternedPathOrHandle::Owned(h));
206217
result
207218
}
208219

209220
pub fn append(&mut self, appendables: &mut Appendables<H>, appendage: H) {
210-
self.appendages
211-
.push_front(&mut appendables.0, PathOrAppendable::Handle(appendage));
221+
self.appendages.push_front(
222+
&mut appendables.elements,
223+
InternedPathOrHandle::Database(appendage),
224+
);
212225
}
213226
}
214227

@@ -231,8 +244,8 @@ where
231244
{
232245
let mut cycles = EnumSet::new();
233246

234-
let end_node = match self.appendages.clone().pop_front(&mut appendables.0) {
235-
Some(appendage) => appendage.end_node(db),
247+
let end_node = match self.appendages.clone().pop_front(&mut appendables.elements) {
248+
Some(appendage) => appendage.end_node(db, &appendables.interned),
236249
None => return Ok(cycles),
237250
};
238251

@@ -242,11 +255,11 @@ where
242255
// get prefix elements
243256
let mut prefix_appendages = List::empty();
244257
loop {
245-
let appendable = appendages.pop_front(&mut appendables.0).cloned();
258+
let appendable = appendages.pop_front(&mut appendables.elements).cloned();
246259
match appendable {
247260
Some(appendage) => {
248-
let is_cycle = appendage.start_node(db) == end_node;
249-
prefix_appendages.push_front(&mut appendables.0, appendage);
261+
let is_cycle = appendage.start_node(db, &appendables.interned) == end_node;
262+
prefix_appendages.push_front(&mut appendables.elements, appendage);
250263
if is_cycle {
251264
break;
252265
}
@@ -257,8 +270,14 @@ where
257270

258271
// build prefix path -- prefix starts at end_node, because this is a cycle
259272
let mut prefix_path = PartialPath::from_node(graph, partials, end_node);
260-
while let Some(appendage) = prefix_appendages.pop_front(&mut appendables.0) {
261-
appendage.append_to(graph, partials, db, &mut prefix_path)?;
273+
while let Some(appendage) = prefix_appendages.pop_front(&mut appendables.elements) {
274+
appendage.append_to(
275+
graph,
276+
partials,
277+
db,
278+
&appendables.interned,
279+
&mut prefix_path,
280+
)?;
262281
}
263282

264283
// build cyclic path

0 commit comments

Comments
 (0)