-
Notifications
You must be signed in to change notification settings - Fork 34
Dealing with shared ownership (Rc and Arc) #37
Comments
Yeah, that's an odd inconsistency. I'm surprised we have an implementation for Arc at all. |
If heapsize is going to cope with If we could guarantee that only one heapsize calculation at a time was needed, we could try to cram the marker into the Rc, but this would have to be done unsafely since the heap_size_of_children function only has |
@asajeffrey What would be the end goal of maintaining that graph? |
Only visiting each enum BTree {
Leaf,
Node(Rc<BTree>, Rc<BTree>),
}
let tree0 = Rc::new(BTree::Leaf);
let tree1 = Rc::new(BTree::Node(tree0.clone(),tree0.clone()));
let tree2 = Rc::new(BTree::Node(tree1.clone(),tree1.clone()));
... If you use tree-traversal on |
And it’s not (just) about traversal time, it’s to avoid duplicating the count of memory used by |
Indeed, we can avoid having to divide by the ref count by only counting each Rc once. |
Firefox's memory profiling still doesn't handle ref-counted types well. For some of the types we return the full size if the refcount is 1, and 0 otherwise. I've thought about doing the divide-by-the-refcount idea (and using integer division) but never gotten around to it. I think using fractional types would be silly and not worth it. In general it is indeed better to under-measure than over-measure, because you can get the total heap size (from the allocator) and then subtract the size of all known heap data structures and end up with an "unknown" bucket (called "heap-unclassified" in Firefox's about:memory) and you want that "unknown" bucket to be accurate. If you over-measure it could go negative, which is confusing. (Double-counting any memory can have similarly bad effects.) In general, ensuring correctness of these measurements is a hard problem. When your code says your memory usage is X, how can you trust that? Firefox has a tool called DMD (https://developer.mozilla.org/en-US/docs/Mozilla/Performance/DMD) that verifies memory reporting by identifying which heap blocks are measure zero times, 1 times, and more than 1 times. It's really important for improving coverage and debugging the measurement code. I was hoping to implement something similar for Rust/Servo but never got around to it, at least partly because it requires (a) wrapping allocations and (b) getting and saving lots of stack traces, both of which are tricky in Rust. |
Aww, what’s wrong with reporting 128.00000000000000004 bytes of memory usage? :) |
Another "interesting" thing is that |
If we're dealing with cyclic structures, then pretty much we have to either do graph marking, or ignore any Rc with refcount > 1. Are we concerned that ignoring Rc with refcount > 1 would cause reported size to change as Rc's are cloned / dropped? |
I think I’d prefer not to have a generic |
@asajeffrey I assume that there's also a third point there - you can choose not to ignore any |
@larsbergstrom yes, you could try to be clever and get metadata about the type, like can it introduce cycles, but it seems like it would be tricky to get to fly without over-engineering it. |
@asajeffrey I just thought it might be worth looking at, since a lot of the stuff we |
As a hack, we could abuse the refcount, with something like:
Yuck. It would probably work, but yuck. |
@asajeffrey @larsbergstrom @SimonSapin @nnethercote @jdm Thanks for the really helpful discussion! I appreciate it! My original question to @larsbergstrom and @asajeffrey (over email) was why The context of my original question is a library for Adapton in Rust (See http://adapton.org and https://github.com/cuplv/adapton.rust specifically). My use of In many cases, I can traverse the shared However, there are many other In sum, what is clear to me now is that I need to exploit the specific invariants and structure of my library to do a non-double-visit traversal of its DAG structure of memoized nodes. Further, I need to think about what invariants will help me do something clever and (hopefully equally) simple to traverse the keys that index these memoized nodes. Thanks again for the discussion! |
Yes, this is the kind of thing I had in mind with “case by case specific contexts”. |
@SimonSapin Yes. This stance ("exploit the invariants of the application domain, and do not attempt to give a generic solution") seems right to me now as well, for what it's worth. ;) |
This crate has always given a lower bound of the actual heap memory usage: we use `#[ignore_heap_size_of]` when not doing it is not practical. This fix #37 with one of the ideas given in the discussion there: return an accurate result when the reference count is 1 (and ownership is in fact not shared), or zero otherwise. However, since APIs to access reference counts are not `#[stable]` yet (see rust-lang/rust#28356), so always return zero when unstable APIs are not enabled.
I’m currently changing Servo so that it can build with |
The removal of this impl is not included in this commit. CC servo/heapsize#37 (comment)
The removal of this impl is not included in this commit. CC servo/heapsize#37 (comment)
The removal of this impl is not included in this commit. CC servo/heapsize#37 (comment)
The removal of this impl is not included in this commit. CC servo/heapsize#37 (comment)
Use parking_lot::RwLock for PropertyDeclarationBlock <!-- Please describe your changes on the following line: --> As discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=1305141 Closes #13176 --- Original PR title: Stop relying on `impl<T: HeapSizeOf> HeapSizeOf for Arc<T>` servo/heapsize#37 (comment) This builds on top of that. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refactor <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13459) <!-- Reviewable:end -->
Use parking_lot::RwLock for PropertyDeclarationBlock <!-- Please describe your changes on the following line: --> As discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=1305141 Closes #13176 --- Original PR title: Stop relying on `impl<T: HeapSizeOf> HeapSizeOf for Arc<T>` servo/heapsize#37 (comment) This builds on top of that. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refactor <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13459) <!-- Reviewable:end -->
The removal of this impl is not included in this commit. CC servo/heapsize#37 (comment)
The removal of this impl is not included in this commit. CC servo/heapsize#37 (comment)
Use parking_lot::RwLock for PropertyDeclarationBlock <!-- Please describe your changes on the following line: --> As discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=1305141 Closes #13176 --- Original PR title: Stop relying on `impl<T: HeapSizeOf> HeapSizeOf for Arc<T>` servo/heapsize#37 (comment) This builds on top of that. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refactor <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13459) <!-- Reviewable:end -->
Use parking_lot::RwLock for PropertyDeclarationBlock <!-- Please describe your changes on the following line: --> As discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=1305141 Closes #13176 --- Original PR title: Stop relying on `impl<T: HeapSizeOf> HeapSizeOf for Arc<T>` servo/heapsize#37 (comment) This builds on top of that. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refactor <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13459) <!-- Reviewable:end -->
…Block (from servo:no-arc-heapsize); r=emilio <!-- Please describe your changes on the following line: --> As discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=1305141 Closes #13176 --- Original PR title: Stop relying on `impl<T: HeapSizeOf> HeapSizeOf for Arc<T>` servo/heapsize#37 (comment) This builds on top of that. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refactor <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: aea9545e16fd6ea4a6b1234d1b969457313a5fa7 --HG-- rename : servo/components/style/domrefcell.rs => servo/components/script/dom/bindings/cell.rs
FWIW, in malloc_size_of I've handled Rc/Arc by allowing users provide a table that records which pointers have been measured. This is working well for Stylo. |
…Block (from servo:no-arc-heapsize); r=emilio <!-- Please describe your changes on the following line: --> As discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=1305141 Closes #13176 --- Original PR title: Stop relying on `impl<T: HeapSizeOf> HeapSizeOf for Arc<T>` servo/heapsize#37 (comment) This builds on top of that. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refactor <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: aea9545e16fd6ea4a6b1234d1b969457313a5fa7 UltraBlame original commit: 0e74d9fe18cf1cbbab7d7410027a051f94eac90f
…Block (from servo:no-arc-heapsize); r=emilio <!-- Please describe your changes on the following line: --> As discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=1305141 Closes #13176 --- Original PR title: Stop relying on `impl<T: HeapSizeOf> HeapSizeOf for Arc<T>` servo/heapsize#37 (comment) This builds on top of that. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refactor <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: aea9545e16fd6ea4a6b1234d1b969457313a5fa7 UltraBlame original commit: 0e74d9fe18cf1cbbab7d7410027a051f94eac90f
…Block (from servo:no-arc-heapsize); r=emilio <!-- Please describe your changes on the following line: --> As discussed in https://bugzilla.mozilla.org/show_bug.cgi?id=1305141 Closes #13176 --- Original PR title: Stop relying on `impl<T: HeapSizeOf> HeapSizeOf for Arc<T>` servo/heapsize#37 (comment) This builds on top of that. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refactor <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: aea9545e16fd6ea4a6b1234d1b969457313a5fa7 UltraBlame original commit: 0e74d9fe18cf1cbbab7d7410027a051f94eac90f
A central idea behind this crate is that most resources in Rust have one well-defined owner.
heap_size_of_children
can trace recursively and only consider owned resources. For&T
references it can return zero since they usually either point to non-heap memory, or to heap memory owned by something else that will be accounted for there.This breaks down for types like
Rc<T>
andArc<T>
that introduce "shared ownership". They do have heap memory that we want to count, but in a fully generic context there is no clear single point where it should be counted.(In a more specific context there count be a "main"
Rc
reference for a given resource. For example, following only the "first child" and "next sibling" references in a Kuchiki tree allows traversing the whole tree without duplication.)In the current version (90dd7e0) this crate has:
This impl is returns an incorrect number for two reasons:
It’s too low because it only counts the size of resources owned by
T
, not the size ofT
itself and the two reference counts thatArc
holds internally. This part is easy to fix.It’s too high because there could be many
Arc
references to the sameT
value, and this impl duplicates by that many times the count of memory owned byT
.One idea that I’ve heard/read (I don’t remember from who, sorry) was to divide by the strong reference count. If all strong references are traced correctly these fractions should add up to the correct value. But the
usize
return type would have to be changed tof32
orf64
to get remotely accurate results, which is weird at best. AndArc::strong_count
andRc::strong_count
are still#[unstable]
: Tracking issue for Arc/Rc extras rust-lang/rust#28356The current version of this crate has another impl:
My understanding of it as that, in practice, when we can’t have a correct impl of
HeapSizeOf
, Servo sometimes implement it (incorrectly) returning zero because an underestimated answer is better(?) than no answer.This
Vec<Rc<T>>
follows this idea, just making the error smaller: don’t countRc
’s but count theVec
containing them.Still, I don’t think there’s a reason to treat
Rc<T>
andArc<T>
differently.The text was updated successfully, but these errors were encountered: