Skip to content

Commit c999c71

Browse files
authored
refactor: Use ThinVec for CycleHeads (#787)
1 parent 2253999 commit c999c71

File tree

2 files changed

+46
-90
lines changed

2 files changed

+46
-90
lines changed

Diff for: src/cycle.rs

+42-82
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
//! cycle head may then iterate, which may result in a new set of iterations on the inner cycle,
5050
//! for each iteration of the outer cycle.
5151
52+
use thin_vec::{thin_vec, ThinVec};
53+
5254
use crate::key::DatabaseKeyIndex;
5355

5456
/// The maximum number of times we'll fixpoint-iterate before panicking.
@@ -97,21 +99,22 @@ pub struct CycleHead {
9799
/// plural in case of nested cycles) representing the cycles it is part of, and the current
98100
/// iteration count for each cycle head. This struct tracks these cycle heads.
99101
#[derive(Clone, Debug, Default)]
100-
#[allow(clippy::box_collection)]
101-
pub struct CycleHeads(Option<Box<Vec<CycleHead>>>);
102+
pub struct CycleHeads(ThinVec<CycleHead>);
102103

103104
impl CycleHeads {
104105
pub(crate) fn is_empty(&self) -> bool {
105-
// We ensure in `remove` and `extend` that we never have an empty hashset, we always use
106-
// None to signify empty.
107-
self.0.is_none()
106+
self.0.is_empty()
108107
}
109108

110109
pub(crate) fn initial(database_key_index: DatabaseKeyIndex) -> Self {
111-
Self(Some(Box::new(vec![CycleHead {
110+
Self(thin_vec![CycleHead {
112111
database_key_index,
113112
iteration_count: 0,
114-
}])))
113+
}])
114+
}
115+
116+
pub(crate) fn iter(&self) -> std::slice::Iter<'_, CycleHead> {
117+
self.0.iter()
115118
}
116119

117120
pub(crate) fn contains(&self, value: &DatabaseKeyIndex) -> bool {
@@ -120,117 +123,74 @@ impl CycleHeads {
120123
}
121124

122125
pub(crate) fn remove(&mut self, value: &DatabaseKeyIndex) -> bool {
123-
let Some(cycle_heads) = &mut self.0 else {
124-
return false;
125-
};
126-
let found = cycle_heads
126+
let found = self
127+
.0
127128
.iter()
128129
.position(|&head| head.database_key_index == *value);
129130
let Some(found) = found else { return false };
130-
cycle_heads.swap_remove(found);
131-
if cycle_heads.is_empty() {
132-
self.0.take();
133-
}
131+
self.0.swap_remove(found);
134132
true
135133
}
136134

135+
pub(crate) fn clear(&mut self) {
136+
self.0.clear();
137+
}
138+
137139
pub(crate) fn update_iteration_count(
138140
&mut self,
139141
cycle_head_index: DatabaseKeyIndex,
140142
new_iteration_count: u32,
141143
) {
142-
if let Some(cycle_head) = self.0.as_mut().and_then(|cycle_heads| {
143-
cycle_heads
144-
.iter_mut()
145-
.find(|cycle_head| cycle_head.database_key_index == cycle_head_index)
146-
}) {
144+
if let Some(cycle_head) = self
145+
.0
146+
.iter_mut()
147+
.find(|cycle_head| cycle_head.database_key_index == cycle_head_index)
148+
{
147149
cycle_head.iteration_count = new_iteration_count;
148150
}
149151
}
150152

151153
#[inline]
152-
pub(crate) fn insert_into(self, cycle_heads: &mut Vec<CycleHead>) {
153-
if let Some(heads) = self.0 {
154-
insert_into_impl(&heads, cycle_heads);
155-
}
156-
}
157-
158154
pub(crate) fn extend(&mut self, other: &Self) {
159-
if let Some(other) = &other.0 {
160-
let heads = &mut **self.0.get_or_insert_with(|| Box::new(Vec::new()));
161-
insert_into_impl(other, heads);
162-
}
163-
}
164-
}
165-
166-
#[inline]
167-
fn insert_into_impl(insert_from: &Vec<CycleHead>, insert_into: &mut Vec<CycleHead>) {
168-
insert_into.reserve(insert_from.len());
169-
for head in insert_from {
170-
if let Some(existing) = insert_into
171-
.iter()
172-
.find(|candidate| candidate.database_key_index == head.database_key_index)
173-
{
174-
assert!(existing.iteration_count == head.iteration_count);
175-
} else {
176-
insert_into.push(*head);
155+
self.0.reserve(other.0.len());
156+
157+
for head in other {
158+
if let Some(existing) = self
159+
.0
160+
.iter()
161+
.find(|candidate| candidate.database_key_index == head.database_key_index)
162+
{
163+
assert!(existing.iteration_count == head.iteration_count);
164+
} else {
165+
self.0.push(*head);
166+
}
177167
}
178168
}
179169
}
180170

181171
impl IntoIterator for CycleHeads {
182172
type Item = CycleHead;
183-
type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
173+
type IntoIter = <ThinVec<Self::Item> as IntoIterator>::IntoIter;
184174

185175
fn into_iter(self) -> Self::IntoIter {
186-
self.0.map(|heads| *heads).unwrap_or_default().into_iter()
187-
}
188-
}
189-
190-
pub struct CycleHeadsIter<'a>(std::slice::Iter<'a, CycleHead>);
191-
192-
impl Iterator for CycleHeadsIter<'_> {
193-
type Item = CycleHead;
194-
195-
fn next(&mut self) -> Option<Self::Item> {
196-
self.0.next().copied()
197-
}
198-
199-
fn last(self) -> Option<Self::Item> {
200-
self.0.last().copied()
176+
self.0.into_iter()
201177
}
202178
}
203179

204-
impl std::iter::FusedIterator for CycleHeadsIter<'_> {}
205-
206180
impl<'a> std::iter::IntoIterator for &'a CycleHeads {
207-
type Item = CycleHead;
208-
type IntoIter = CycleHeadsIter<'a>;
181+
type Item = &'a CycleHead;
182+
type IntoIter = std::slice::Iter<'a, CycleHead>;
209183

210184
fn into_iter(self) -> Self::IntoIter {
211-
CycleHeadsIter(
212-
self.0
213-
.as_ref()
214-
.map(|heads| heads.iter())
215-
.unwrap_or_default(),
216-
)
185+
self.iter()
217186
}
218187
}
219188

220189
impl From<CycleHead> for CycleHeads {
221190
fn from(value: CycleHead) -> Self {
222-
Self(Some(Box::new(vec![value])))
223-
}
224-
}
225-
226-
impl From<Vec<CycleHead>> for CycleHeads {
227-
fn from(value: Vec<CycleHead>) -> Self {
228-
Self(if value.is_empty() {
229-
None
230-
} else {
231-
Some(Box::new(value))
232-
})
191+
Self(thin_vec![value])
233192
}
234193
}
235194

236-
pub(crate) const EMPTY_CYCLE_HEADS: CycleHeads = CycleHeads(None);
195+
pub(crate) static EMPTY_CYCLE_HEADS: std::sync::LazyLock<CycleHeads> =
196+
std::sync::LazyLock::new(|| CycleHeads(ThinVec::new()));

Diff for: src/function/maybe_changed_after.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ where
343343
return VerifyResult::Changed;
344344
}
345345

346-
let mut cycle_heads = vec![];
346+
let mut cycle_heads = CycleHeads::default();
347347
'cycle: loop {
348348
// Fully tracked inputs? Iterate over the inputs and check them, one by one.
349349
//
@@ -361,7 +361,7 @@ where
361361
{
362362
VerifyResult::Changed => break 'cycle VerifyResult::Changed,
363363
VerifyResult::Unchanged(input_accumulated, cycles) => {
364-
cycles.insert_into(&mut cycle_heads);
364+
cycle_heads.extend(&cycles);
365365
inputs |= input_accumulated;
366366
}
367367
}
@@ -418,11 +418,7 @@ where
418418
// from cycle heads. We will handle our own memo (and the rest of our cycle) on a
419419
// future iteration; first the outer cycle head needs to verify itself.
420420

421-
let in_heads = cycle_heads
422-
.iter()
423-
.position(|&head| head.database_key_index == database_key_index)
424-
.inspect(|&head| _ = cycle_heads.swap_remove(head))
425-
.is_some();
421+
let in_heads = cycle_heads.remove(&database_key_index);
426422

427423
if cycle_heads.is_empty() {
428424
old_memo.mark_as_verified(
@@ -443,7 +439,7 @@ where
443439
}
444440
break 'cycle VerifyResult::Unchanged(
445441
InputAccumulatedValues::Empty,
446-
CycleHeads::from(cycle_heads),
442+
cycle_heads,
447443
);
448444
}
449445
}

0 commit comments

Comments
 (0)