Skip to content

Commit

Permalink
Handle self-loops in spanning tree representation
Browse files Browse the repository at this point in the history
- Limit atom sizes in cache and drop them upon exiting thread
  • Loading branch information
benruijl committed Nov 6, 2024
1 parent 60b1ece commit dc2d96c
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 10 deletions.
13 changes: 13 additions & 0 deletions src/atom/representation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,19 @@ impl Atom {
_ => unreachable!("Unknown type {}", raw[0]),
}
}

/// Get the capacity of the underlying buffer.
pub(crate) fn get_capacity(&self) -> usize {
match self {
Atom::Num(n) => n.data.capacity(),
Atom::Var(v) => v.data.capacity(),
Atom::Fun(f) => f.data.capacity(),
Atom::Mul(m) => m.data.capacity(),
Atom::Add(a) => a.data.capacity(),
Atom::Pow(p) => p.data.capacity(),
Atom::Zero => 0,
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down
7 changes: 6 additions & 1 deletion src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ impl SpanningTree {
let node = self.nodes[n].back_edges[back_edge_index];
back_edge_index += 1;

if self.nodes[node].parent == n {
if node == n {
// self-loop
continue;
}

Expand Down Expand Up @@ -390,6 +391,10 @@ impl<N, E> Graph<N, E> {
edge.vertices.0
};

if n == target {
tree_nodes[n].back_edges.push(n);
}

if tree_nodes[target].position.is_none() {
nodes_to_visit.push((target, n));
}
Expand Down
14 changes: 5 additions & 9 deletions src/state.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use byteorder::{ReadBytesExt, WriteBytesExt};
use std::hash::Hash;
use std::io::{Read, Write};
use std::mem::ManuallyDrop;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, RwLock};
use std::thread::LocalKey;
Expand Down Expand Up @@ -73,11 +72,8 @@ static VARIABLE_LISTS: AppendOnlyVec<Arc<Vec<Variable>>> = AppendOnlyVec::new();
static SYMBOL_OFFSET: AtomicUsize = AtomicUsize::new(0);

thread_local!(
/// A thread-local workspace, that stores recyclable atoms. By making it const and
/// `ManuallyDrop`, the fastest implementation is chosen for the current platform.
/// In principle this leaks memory, but Symbolica only uses thread pools that live as
/// long as the main thread, so this is no issue.
static WORKSPACE: ManuallyDrop<Workspace> = const { ManuallyDrop::new(Workspace::new()) }
/// A thread-local workspace, that stores recyclable atoms.
static WORKSPACE: Workspace = const { Workspace::new() }
);

/// A global state, that stores mappings from variable and function names to ids.
Expand Down Expand Up @@ -713,7 +709,7 @@ pub struct Workspace {

impl Workspace {
const ATOM_BUFFER_MAX: usize = 30;
const ATOM_CACHE_SIZE_MAX: usize = 10_000_000;
const ATOM_CACHE_SIZE_MAX: usize = 20_000_000;

/// Create a new workspace.
const fn new() -> Self {
Expand All @@ -724,7 +720,7 @@ impl Workspace {

/// Get a thread-local workspace.
#[inline]
pub fn get_local() -> &'static LocalKey<ManuallyDrop<Workspace>> {
pub fn get_local() -> &'static LocalKey<Workspace> {
LicenseManager::check();

&WORKSPACE
Expand Down Expand Up @@ -848,7 +844,7 @@ impl Drop for RecycledAtom {
return;
}

if self.0.as_view().get_byte_size() > Workspace::ATOM_CACHE_SIZE_MAX {
if self.0.get_capacity() > Workspace::ATOM_CACHE_SIZE_MAX {
return;
}

Expand Down

0 comments on commit dc2d96c

Please sign in to comment.