Skip to content

Commit

Permalink
Use ahash with IndexSet in simple_cycles() (#688)
Browse files Browse the repository at this point in the history
We recently merged a new simple_cycles() function in #633 that added an
implementation of johnson's algorithm for finding all the simple cycles
in a directed graph. In that function we used IndexSet to have a set
with O(1) lookup with a deterministic ordering. As part of that we used
the default hashing algorithm from the Rust stdlib. This leaves some
performance on the table as the stdlib hashing algorithm is slower in
order to have proven resistance to HashDoS attacks. Since this is
relevant for the internal usage in Johnson's algorithm this commit
switches the hasing algorithm to ahash (which is the default hashbrown
uses) which offers better performance.
  • Loading branch information
mtreinish authored Sep 26, 2022
1 parent 676a7cd commit ced0310
Showing 1 changed file with 8 additions and 6 deletions.
14 changes: 8 additions & 6 deletions src/connectivity/johnson_simple_cycles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub struct SimpleCycleIter {
blocked: HashSet<NodeIndex>,
closed: HashSet<NodeIndex>,
block: HashMap<NodeIndex, HashSet<NodeIndex>>,
stack: Vec<(NodeIndex, IndexSet<NodeIndex>)>,
stack: Vec<(NodeIndex, IndexSet<NodeIndex, ahash::RandomState>)>,
start_node: NodeIndex,
node_map: HashMap<NodeIndex, NodeIndex>,
reverse_node_map: HashMap<NodeIndex, NodeIndex>,
Expand Down Expand Up @@ -115,7 +115,8 @@ fn unblock(
blocked: &mut HashSet<NodeIndex>,
block: &mut HashMap<NodeIndex, HashSet<NodeIndex>>,
) {
let mut stack: IndexSet<NodeIndex> = IndexSet::new();
let mut stack: IndexSet<NodeIndex, ahash::RandomState> =
IndexSet::with_hasher(ahash::RandomState::new());
stack.insert(node);
while let Some(stack_node) = stack.pop() {
if blocked.remove(&stack_node) {
Expand All @@ -141,7 +142,7 @@ fn unblock(
#[allow(clippy::too_many_arguments)]
fn process_stack(
start_node: NodeIndex,
stack: &mut Vec<(NodeIndex, IndexSet<NodeIndex>)>,
stack: &mut Vec<(NodeIndex, IndexSet<NodeIndex, ahash::RandomState>)>,
path: &mut Vec<NodeIndex>,
closed: &mut HashSet<NodeIndex>,
blocked: &mut HashSet<NodeIndex>,
Expand All @@ -165,7 +166,7 @@ fn process_stack(
next_node,
subgraph
.neighbors(next_node)
.collect::<IndexSet<NodeIndex>>(),
.collect::<IndexSet<NodeIndex, ahash::RandomState>>(),
));
closed.remove(&next_node);
blocked.insert(next_node);
Expand Down Expand Up @@ -206,7 +207,8 @@ impl SimpleCycleIter {
}));
}
// Restore previous state if it exists
let mut stack: Vec<(NodeIndex, IndexSet<NodeIndex>)> = std::mem::take(&mut slf.stack);
let mut stack: Vec<(NodeIndex, IndexSet<NodeIndex, ahash::RandomState>)> =
std::mem::take(&mut slf.stack);
let mut path: Vec<NodeIndex> = std::mem::take(&mut slf.path);
let mut closed: HashSet<NodeIndex> = std::mem::take(&mut slf.closed);
let mut blocked: HashSet<NodeIndex> = std::mem::take(&mut slf.blocked);
Expand Down Expand Up @@ -267,7 +269,7 @@ impl SimpleCycleIter {
slf.start_node,
subgraph
.neighbors(slf.start_node)
.collect::<IndexSet<NodeIndex>>(),
.collect::<IndexSet<NodeIndex, ahash::RandomState>>(),
)];
if let Some(res) = process_stack(
slf.start_node,
Expand Down

0 comments on commit ced0310

Please sign in to comment.