perf: remove GcRefCell from inline cache to avoid borrow checking overhead#5400
perf: remove GcRefCell from inline cache to avoid borrow checking overhead#5400mansiverma897993 wants to merge 4 commits into
Conversation
Test262 conformance changes
Tested main commit: |
|
Thanks for this @mansiverma897993, you need to run rust fmt |
|
For the clone it feels inefficient to take the value out, put a default one in then add the value back on a hotpath. We may need to get a pointer into the cell here as its safe (we are taking and resetting in the same operation). Something like this: fn clone(&self) -> Self {
// SAFETY: `entries` is only ever accessed through `&self`/`&mut self`
// on this single-threaded cache, and cloning `CacheEntry` doesn't
// reenter this `Cell`, so it's safe to read through the raw pointer
// for the duration of this borrow without disturbing the cell's contents.
let cloned_entries = unsafe { (*self.entries.as_ptr()).clone() };
Self {
name: self.name.clone(),
entries: Cell::new(cloned_entries),
megamorphic: self.megamorphic.clone(),
}
}Also, while you're here, can you move the megmorphic to be the first item in the struct? The |
|
@jasonwilliams I have sucessfully update the PR as per your accordance !! look at once |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5400 +/- ##
===========================================
+ Coverage 47.24% 60.30% +13.05%
===========================================
Files 476 567 +91
Lines 46892 63162 +16270
===========================================
+ Hits 22154 38089 +15935
- Misses 24738 25073 +335 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@mansiverma897993 thanks, I'm not sure when I can take a look again. In the meantime are you able to compare the benchmarks between main and this? We have a data repo here: https://github.com/boa-dev/data You can check that out in an adjacent folder and run boa against the bench/bench-v8/combined.js file When I checked on cachegrind it looked like there were still a high amount of cache misses happening somewhere in that function, maybe @HalidOdat is better at hunting these things down. |
| } | ||
|
|
||
| let mut entries = self.entries.borrow_mut(); | ||
| let mut entries = self.entries.take(); |
There was a problem hiding this comment.
take on Cell performs a copy i think. perhaps its not trivial to copy the entire cache twice (once here once below for setting) everytime you try to access or set it?
Given how often this is used.
There was a problem hiding this comment.
"Thanks for pointing this out! You are absolutely right, Cell::take() does copy the entire ArrayVec which adds overhead on the hot path.
To avoid this copy while still avoiding GcRefCell's borrow checking, we can use Cell::as_ptr() to mutate the entries in-place using a raw pointer:
rust
let entries = unsafe { &mut *self.entries.as_ptr() };
Since this is single-threaded and the reference doesn't escape the function, this should be safe and provide zero-copy access. I'll update the PR with this change!" I am updating PR accordingly I am working on it !!
There was a problem hiding this comment.
@zhuzhu81998 @jasonwilliams I have updated PR accordingly now look at once!!
This Pull Request fixes/closes #5399.
It changes the following:
GcRefCell<ArrayVec<CacheEntry, PIC_CAPACITY>>withCell<ArrayVec<CacheEntry, PIC_CAPACITY>>inInlineCachestructure.Cell::take()andCell::set()to access and modify the inline cache entries in a zero-overhead manner, avoiding all dynamic borrow-checking overhead on the hot path.CloneandDebugforInlineCachebecauseCell<T>only auto-derives them whenT: Copy, andArrayVecis non-Copy..entries.borrow()with a new test helper.entries()incore/engine/src/vm/inline_cache/tests.rsto allow unit testing without borrow checks.