-
-
Notifications
You must be signed in to change notification settings - Fork 413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GcRefCell.borrow_mut is too expensive #2773
Comments
Yeah, that's inherent of our current GC design. We have an open issue (#2631) about investigating other API designs for our GC, which should allow us to solve issues like this. |
Hey @tunz thanks for raising this, that does indeed sound quite expensive so I’m glad you brought it up. I’d be interested in seeing if we can solve this somehow. I agree gc object within a gc object doesn’t sound ideal.
could you elaborate on this idea a bit more? How would this work with objects that have multiple references? |
It's not a clear idea yet. I was thinking of this scenario. First, make #[derive(Trace, Finalize)]
struct Obj {
GcSlot<i32> v,
}
let num = Gc::new(123);
let v = Gc::new(Obj { v: num.into() }); And, BoaGc has a vector of gc box pointers which is called roots. When Gc object is created, it adds the pointer of a created GcBox object to the roots. When Gc object is dropped, it removes the pointer from the roots (or decrease a reference count). When gc is triggered, it iterates the roots and marks all the referencing objects recursively. So, objects referenced by other multiple objects should be just handled by the mark and sweep process. But, one thing I'm not sure is safety. The assumption of this approach is that we must create all the traceable objects with |
Oh I think I got the gist of your idea: to have a I think it should be possible to implement a POC of this design, but as you've said, there's safety concerns about keeping |
I did a quick research about other gc implementations written in Rust, and found that shredder has an interesting approach to find roots. It creates a I tried a quick prototype for this, but I'm stuck at understanding weak pointer implementation. Especially, the following line. Line 289 in 86726f1
Is this condition correct? It looks weird that it runs finalizers for weak objects when |
Attempt to address the issue boa-dev#2773. The existing implementation had an expensive overhead of managing root counts, especially for mutable borrow of GcRefCell. Instead of managing the root counts, this change counts the number of Gc/WeakGc handles located in Gc heap objects and total number of them. Then, we can find whether there is a root by comparing those numbers.
Attempt to address the issue boa-dev#2773. The existing implementation had an expensive overhead of managing root counts, especially for mutable borrow of GcRefCell. Instead of managing the root counts, this change counts the number of Gc/WeakGc handles located in Gc heap objects and total number of them. Then, we can find whether there is a root by comparing those numbers.
Here’s a blogpost with a novel rust Garbage Collection mechanism for finding roots: https://coredumped.dev/2022/04/11/implementing-a-safe-garbage-collector-in-rust/ |
I believe the reported issue is fixed. It should be good to close. |
* Find roots when running GC Attempt to address the issue #2773. The existing implementation had an expensive overhead of managing root counts, especially for mutable borrow of GcRefCell. Instead of managing the root counts, this change counts the number of Gc/WeakGc handles located in Gc heap objects and total number of them. Then, we can find whether there is a root by comparing those numbers. * Fix clippy errors * Keep reference counts in Box * Addressing comment * Fix clippy errors * Fix typo * non_root_count includes mark bit * give a space
When
Object.borrow_mut()
is called, boa tries to root/unroot all the properties. So, even if we just want to set a single element, boa accesses all the other elements. I see that this overhead is taking almost 90% of run time in QuickJS NavierStokes benchmark.In the following example, running
arr[0] = {};
is much slower when there are more elements.I guess this design is for such case that we add a
Gc<GcRefCell<T>>
object to anotherGc<GcRefCell<T>>
object.When
123
object is pushed to thev
object, it should be unrooted becausev
is the root now. But,Vec.push
does not know that information, and only theGcRefMut
returned byborrow_mut
knows that. So, what it does is just to root all the children when borrow starts, and unroot all the children again when borrow ends.I'm not sure what's the best approach to fix this issue. I personally think we should not use
Gc
in anotherGc
object. I think we should allowGc
objects to keepGcPointer
instead, and manage roots in a single place.The text was updated successfully, but these errors were encountered: