Skip to content

Commit 0454fc7

Browse files
committed
perf: avoid Rc refcount overhead in DomSlot chain traversal
Traverse the DynamicDomSlot chain using raw pointers instead of Rc::clone/drop per hop. The chain is transitively kept alive by the borrowed &self, so raw pointer access is sound.
1 parent 73a970b commit 0454fc7

1 file changed

Lines changed: 20 additions & 14 deletions

File tree

packages/yew/src/dom_bundle/position.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -196,22 +196,28 @@ impl DynamicDomSlot {
196196
}
197197

198198
fn with_next_sibling<R>(&self, f: impl FnOnce(Option<&Node>) -> R) -> R {
199-
// we use an iterative approach to traverse a possible long chain for references
200-
// see for example issue #3043 why a recursive call is impossible for large lists in vdom
201-
202-
// TODO: there could be some data structure that performs better here. E.g. a balanced tree
203-
// with parent pointers come to mind, but they are a bit fiddly to implement in rust
204-
let mut this = self.target.clone();
199+
// We use an iterative approach to traverse a possible long chain of references.
200+
// See issue #3043 for why a recursive call is impossible for large lists in vdom.
201+
//
202+
// We traverse via raw pointers to avoid Rc refcount overhead (clone + drop) per hop.
203+
//
204+
// SAFETY: All RefCells in the chain are valid for the duration of this traversal:
205+
// - `self.target` (Rc) is alive because `self` is borrowed
206+
// - Each DomSlot::Chained(DynamicDomSlot { target }) in the chain holds a strong Rc to the
207+
// next RefCell, so all links are transitively kept alive
208+
// - Yew is single-threaded and this function does not yield, so no mutable borrow (e.g.
209+
// from reassign()) can occur on any RefCell in the chain during traversal
210+
// - Each RefCell::borrow() is dropped before advancing to the next hop
211+
let mut ptr: *const RefCell<DomSlot> = Rc::as_ptr(&self.target);
205212
loop {
206-
// v------- borrow lives for this match expression
207-
let next_this = match &this.borrow().variant {
213+
let cell = unsafe { &*ptr };
214+
let slot_ref = cell.borrow();
215+
match &slot_ref.variant {
208216
DomSlotVariant::Node(ref n) => break f(n.as_ref()),
209-
// We clone an Rc here temporarily, so that we don't have to consume stack
210-
// space. The alternative would be to keep the
211-
// `Ref<'_, DomSlot>` above in some temporary buffer
212-
DomSlotVariant::Chained(ref chain) => chain.target.clone(),
213-
};
214-
this = next_this;
217+
DomSlotVariant::Chained(ref chain) => {
218+
ptr = Rc::as_ptr(&chain.target);
219+
}
220+
}
215221
}
216222
}
217223
}

0 commit comments

Comments
 (0)