I'm not sure I'll have time to work on faster handles, but essentially the idea is to use a large linear area as handle backing. On 64 bit machines, virtual space is not very limited, so this makes more sense now than in the early days of V8. By using the right mmap flags you can make it auto-grow like a machine stack, so it only uses the high water memory. There would be a guard page that segfaults for unbounded growth (see below).
This would simplify the code generated for both handle creation and HandleScope destruction by quite a bit. As a thought experiment I have some handle-like code at
https://godbolt.org/z/nWK7346TP Three lines are commented out, so the initial state of the Godbolt example is what would happen if we had linear handle backing. If you remove the comment punctuation on those three lines you get something more like what we have now, where a check of the limit is inlined, and there's an out-of-line call to extend the backing with a new block. The generated code becomes much worse. (The example uses Local<long>, obviously in reality the handles would be Local<somepointertype>.)
The reason the linear code is so much nicer is that the compiler has full insight into what is going on. It can see that handle slots don't alias if they come from different HandleScope::CreateHandle calls. Creating a Handle becomes not much more than a pointer bump, and closing a scope is not much more than resetting a pointer.
There are some obvious issues with the idea and I have some ideas to fix them:
* I would make it so that the embedder can attach and detach handle scope backings. This way you can only have one linear handle scope backing per thread or perhaps CPU. Obviously this only makes sense back at the event loop where the area is empty anyway. We can default to creating a linear area in the Locker if there is not already one. The Unlocker (which I think is not very heavily used) might need some modification too.
* Memory used is the high water mark of handle creation, which could be wasteful. There are probably places (again, the event loop) where we can release some of the higher pages with an madvise call.
* Some patterns use arbitrary numbers of handles:
* One of these is having a loop that doesn't have a handle scope at the top of it. In this case if HandleScopes were much cheaper it would make sense to put a handle scope at the top of the loop. Hitting the guard page should show us where this needs fixing.
* Another is collections that use handles as elements. There are not too many cases of this in the code base. They should be replaced with a single handle that holds a reference to a FixedArray. If this is too slow (on-heap allocation, write barriers, etc.) then a new collection should be made where only the first N elements are put on-stack and use handle backing. Above a certain size they turn into a reference to an on-heap FixedArray. This is a bit like
https://chromium.googlesource.com/chromium/blink/+/refs/heads/main/Source/wtf/Vector.h which has a templated inline capacity and uses allocation for bigger arrays. You get the speed of on-stack operations for small vectors, and the flexibility of arbitrarily sized on-heap vectors when needed. The off-heap handles need to be reserved at collection creation time so we can push onto the collection inside inner HandleScopes.
* LocalHeap needs its own linear area. This is not a big issue for us as we mainly run V8 in single-threaded mode. For Chrome it means a linear area per thread.
* PersistentHandlesScope would have to be done in a different way. Currently several of the uses of PersistentHandlesScope are immediately followed by ReopenAndCanonicalizeHandlesInNewScope. It seems like this function could move the backings to a new area that was not in the linear handle backing. This one is probably the hardest to solve unless the number of handles in a PersistentHandlesScope can be bounded.
Those are my ideas. Like I said, I don't have much time for this at the moment, so it's on a back-burner. Happy to take a meeting to flesh out the idea though. And perhaps there's a huge issue I have overlooked.