-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Reverse the order of GC compaction cursor movement #5637
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
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96dc850
to
6cef48d
Compare
tenderlove
reviewed
Mar 21, 2022
nobu
reviewed
Mar 22, 2022
4e0bd5e
to
96375cf
Compare
96375cf
to
b1a5105
Compare
0409056
to
be917c4
Compare
This commit changes the way compaction moves objects and sweeps pages in order to better facilitate object movement between size pools. Previously we would move the scan cursor first until we found an empty slot and then we'd decrement the compact cursor until we found something to move into that slot. We would sweep the page that contained the scan cursor before trying to fill it In this algorithm we first move the compact cursor down until we find an object to move - We then take a free page from the desired destination heap (always the same heap in this current iteration of the code). If there is no free page we sweep the page at the sweeping_page cursor, add it to the free pages, and advance the cursor to the next page, and try again. We sweep one page from each size pool in this way, and then repeat that process until all the size pools are compacted (all the cursors have met), and then we update references and sweep the rest of the heap.
be917c4
to
034aae6
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Reverse the order of GC Compaction cursor movement
Summary
The current compaction algorithm works by walking up the heap until it finds a slot to fill and then asking "what object can I use to fill this slot"
This PR reverses the cursor movement to walk down the heap until it gets to a moveable live object and then asking "where is the best place to put this object"
This animation shows a very simplified view of what we mean by "reversing the cursor movement"
Rationale
The current approach works well for a single heap containing fixed width slots because both empty and filled slots within that heap are a known and consistent size, and there is only a single set of scan/compact cursors to manage.
Now that the memory space contains multiple size pools, each containing their own heap and scan/compact cursors the current approach has some problems:
Moving objects between pools is hard
Objects that are mutable, such as strings, can be made larger(eg. using
String#<<
). Currently, when this modification happens we have no way of moving the object to a larger size pool, so we fall back to usingmalloc
to allocate the buffer. By doing this we lose the cache locality benefits that VWA provides.The current compaction algorithm makes moving these objects hard. by starting with an empty slot of a known size, determining what to fill it with (an object from the same heap, or from a different heap that wants to move) would involve either more heap scans, or a data structure to keep track of resized objects that can move between pools.
Only the first size pool (40 byte slot_size) is being swept
The current compaction algorithm uses a global flag attached to
rb_objspace
. When compaction is triggered, the flag is set true, and compaction starts scanning the size pools (starting with the smallest) until the scan/compact cursors have met.As soon as the first size pool has finished compacting and the cursors have met, the current implementation assumes that compaction has finished, updates the references and disables the global compaction flag, resulting in no compaction of any size pools other than the smallest.
Implementation
This PR implements an algorithm that flips the order in which we consider the scan and compact cursors. That is: Rather than discovering an empty slot and attempting to fill it, we discover an object that can move, and then find an appropriate place for it.
The outline of the new algorithm (in pseudo-Ruby) looks like this:
Some considerations of the new approach are:
In this initial implementation the destination heap is always the same as the source heap. No movement between size pools is taking place. This is for backward compatibility with the existing compaction algorithm. Movement between size pools will be added as a seperate feature.
We compact a single page from each pool in the loop rather than compacting one pool to completion and then moving to the next. This is to give objects that wish to move between pools chance to do so. If we compacted an entire heap before moving to the next, we risk filling the heap and not providing opportunity for objects in different heaps the chance to move into it.
Measuring compaction
Railsbench heap visualisation
We wrote a tool to help visualise the heap before and after compaction.
We ran a patched version of railsbench to dump the heap before and after compaction on master and this branch.
Colours in the visualisation correspond to different size pools. Pools are ordered from left to right - 40 bytes, 80 bytes, 160 bytes, 320 bytes. Empty slots are white, and pinned slots are coloured black.
Results are:
Before compaction (master)
After compaction (master)
Before compaction (this PR)
After compaction (this PR)
Discourse benchmarks
We ran Discourse benchmarks for both master and this PR, with
GC.auto_compact=true
configured inconfig/boot.rb
.Benchmarks were run using
We can see a slight slowdown when using auto_compact on this branch when compared with master. This can be attributed to the extra work the compactor is now doing as it's compacting all 4 size pools rather than just the first one.
Raw results are as follows:
What's next