Skip to content

Commit 9a9634d

Browse files
committed
[simple] While tracing, use recursion instead of an explicit stack
Apparently an explicit stack has a ton of performance overhead. This is already what the copying collector, so now we have much *fairer comparison numbers*. This risks stack overflow for long chains of objects. This was already a potential bug in the copying collector!!!! At this point mark/sweep runs binary_trees (n=21) in 28 s This is only 5 seconds slower (than the copying collector (23 s). According to perf report, we spend 30% of our time tracing. We are 18% slower than the copying collector, so lazy tracing could definitely close the remaining gap :)
1 parent 9dd9b73 commit 9a9634d

File tree

2 files changed

+16
-29
lines changed

2 files changed

+16
-29
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ Cargo.lock
99

1010
# Bench
1111
bench/*.class
12+
perf.data

libs/simple/src/lib.rs

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ unsafe impl GarbageCollectionSystem for SimpleCollector {
7373
}
7474

7575
trait DynTrace {
76-
fn trace<'a>(&mut self, visitor: &mut MarkVisitor<'a>);
76+
fn trace(&mut self, visitor: &mut MarkVisitor);
7777
}
7878
impl<T: Trace + ?Sized> DynTrace for T {
79-
fn trace<'a>(&mut self, visitor: &mut MarkVisitor<'a>) {
79+
fn trace(&mut self, visitor: &mut MarkVisitor) {
8080
let Ok(()) = self.visit(visitor);
8181
}
8282
}
@@ -260,7 +260,6 @@ impl RawSimpleCollector {
260260
let mut task = CollectionTask {
261261
id: self.id,
262262
roots: self.shadow_stack.borrow().0.clone(),
263-
gray_stack: Vec::with_capacity(64),
264263
heap: &self.heap
265264
};
266265
task.run();
@@ -273,36 +272,17 @@ enum GreyElement {
273272
struct CollectionTask<'a> {
274273
id: SimpleCollectorId,
275274
roots: Vec<*mut dyn DynTrace>,
276-
gray_stack: Vec<GreyElement>,
277275
heap: &'a GcHeap
278276
}
279277
impl<'a> CollectionTask<'a> {
280278
fn run(&mut self) {
281-
self.gray_stack.extend(self.roots.iter()
282-
.map(|&ptr| GreyElement::Other(ptr)));
283279
// Mark
284-
while let Some(target) = self.gray_stack.pop() {
280+
for &root in &self.roots {
285281
let mut visitor = MarkVisitor {
286282
id: self.id,
287-
gray_stack: &mut self.gray_stack,
288-
};
289-
match target {
290-
GreyElement::Object(obj) => {
291-
unsafe {
292-
debug_assert_eq!((*obj).state, MarkState::Grey);
293-
((*obj).type_info.trace_func)(
294-
&mut *(*obj).value(),
295-
&mut visitor
296-
);
297-
// Mark the object black now it's innards have been traced
298-
(*obj).state = MarkState::Black;
299-
}
300-
},
301-
GreyElement::Other(target) => {
302-
// Dynamically dispatched
303-
unsafe { (*target).trace(&mut visitor); }
304-
}
305283
};
284+
// Dynamically dispatched
285+
unsafe { (*root).trace(&mut visitor); }
306286
}
307287
// Sweep
308288
unsafe { self.heap.allocator.sweep(&self.roots) };
@@ -312,11 +292,10 @@ impl<'a> CollectionTask<'a> {
312292
}
313293
}
314294

315-
struct MarkVisitor<'a> {
295+
struct MarkVisitor {
316296
id: SimpleCollectorId,
317-
gray_stack: &'a mut Vec<GreyElement>,
318297
}
319-
unsafe impl<'a> GcVisitor for MarkVisitor<'a> {
298+
unsafe impl GcVisitor for MarkVisitor {
320299
type Err = !;
321300

322301
fn visit_gc<T, Id>(&mut self, gc: &mut zerogc::Gc<'_, T, Id>) -> Result<(), Self::Err>
@@ -346,7 +325,14 @@ unsafe impl<'a> GcVisitor for MarkVisitor<'a> {
346325
* It will be processed later
347326
*/
348327
(*obj).state = MarkState::Grey;
349-
self.gray_stack.push(GreyElement::Object(obj));
328+
unsafe {
329+
T::trace(
330+
&mut *((*obj).value() as *mut T),
331+
&mut *self
332+
);
333+
}
334+
// Mark the object black now it's innards have been traced
335+
(*obj).state = MarkState::Black;
350336
}
351337
},
352338
MarkState::Grey => {

0 commit comments

Comments
 (0)