Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/undirected/connected_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::marker::PhantomData;

use rustc_hash::{FxHashMap, FxHashSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};

/// A connected component implementation for various generic types.
///
Expand Down Expand Up @@ -60,7 +60,9 @@ where
#[must_use]
pub fn separate_components(groups: &[It]) -> (HashMap<&N, usize>, Vec<usize>) {
let mut table = (0..groups.len()).collect::<Vec<_>>();
let mut indices = HashMap::new();
// Pre-size the hash map to reduce reallocations
let estimated_capacity = groups.iter().map(|g| g.into_iter().count()).sum();
let mut indices = HashMap::with_capacity(estimated_capacity);
Comment on lines +63 to +65
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The capacity estimation here iterates through all groups twice - once to count the elements for pre-allocation (line 64) and again in the main loop (line 66). The into_iter().count() call on line 64 will consume or clone each iterator, potentially causing performance overhead that negates the benefit of pre-allocation.

Consider storing the group sizes during the first pass or accepting that the pre-allocation might not be worth the extra iteration cost in all cases. Alternatively, if groups are cheap to iterate, document that this optimization assumes iteration is inexpensive.

Suggested change
// Pre-size the hash map to reduce reallocations
let estimated_capacity = groups.iter().map(|g| g.into_iter().count()).sum();
let mut indices = HashMap::with_capacity(estimated_capacity);
let mut indices = HashMap::new();

Copilot uses AI. Check for mistakes.
for (mut group_index, group) in groups.iter().enumerate() {
let mut is_empty = true;
for element in group {
Expand Down Expand Up @@ -103,7 +105,10 @@ where
#[must_use]
pub fn components(groups: &[It]) -> C2 {
let (_, gindices) = Self::separate_components(groups);
let mut gb: FxHashMap<usize, FxHashSet<N>> = FxHashMap::default();
// Pre-size the hash map to reduce reallocations
let estimated_capacity = gindices.iter().filter(|&&n| n != usize::MAX).count();
let mut gb: FxHashMap<usize, FxHashSet<N>> =
FxHashMap::with_capacity_and_hasher(estimated_capacity, FxBuildHasher);
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FxHashMap type is a type alias that already includes FxBuildHasher as its default hasher, so you can simply use FxHashMap::with_capacity(estimated_capacity) instead of the more verbose FxHashMap::with_capacity_and_hasher(estimated_capacity, FxBuildHasher). This follows the same pattern as the standard HashMap::with_capacity() used in the separate_components function above.

Suggested change
FxHashMap::with_capacity_and_hasher(estimated_capacity, FxBuildHasher);
FxHashMap::with_capacity(estimated_capacity);

Copilot uses AI. Check for mistakes.
for (i, n) in gindices
.into_iter()
.enumerate()
Expand Down
Loading