[Feature #20470] Extract Ruby's Garbage Collector #10721
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.
Background
As described in [Feature #20351], we are working on the ability to plug alternative garbage collector implementations into Ruby. Our goal is to allow developers and researchers to create and experiment with new implementations of garbage collectors in Ruby in a simplified way. This will also allow experimentation with different GC implementations in production systems so users can choose the best GC implementation for their workloads.
Implementation
In this patch, we have split the current
gc.c
file into two files:gc.c
andgc_impl.c
.gc.c
now only contains code not specific to Ruby GC. This includes code to mark objects (which the GC implementation may choose not to use) and wrappers for internal APIs that the implementation may need to use (e.g. locking the VM).gc_impl.c
now contains the implementation of Ruby's GC. This includes marking, sweeping, compaction, and statistics. Most importantly,gc_impl.c
only uses public APIs in Ruby and a limited set of functions exposed ingc.c
. This allows us to buildgc_impl.c
independently of Ruby and plug Ruby's GC into itself.Demonstration
After checking out the branch, we can first configure with
--with-shared-gc
:Let's now change the slot size of the GC to 64 bytes:
$ sed -i 's/\(#define BASE_SLOT_SIZE\).*/\1 64/' gc_impl.c
We can compile
gc_impl.c
independently using the following commands for clang or gcc (you may have to change the last-I
to match your architecture and platform):We can see that by default, the slot size is 40 bytes and objects are 40 bytes in size:
We can now load our new GC using the
RUBY_GC_LIBRARY_PATH
environment variable (note that you may have to change the path to the DSO):Benchmark
Benchmarks were ran on commit c78cebb on Ubuntu 22.04 using yjit-bench on commit cc5a76e.
Compiling gc_impl branch without
--with-shared-gc
(i.e. how the default Ruby is built), the benchmarks show little to no decrease in performance, with most of it being 0% to 1% slower:Compiling gc_impl branch with
--with-shared-gc
and loading Ruby's current GC usingRUBY_GC_LIBRARY_PATH
, the benchmarks are still fairly good with performance decrease of only around 1% to 2%:Limitations
We recognize that our current implementation does not yet offer the flexibility required for a generic plug-in GC. Specifically, the set of APIs that the plug-in GC has to implement is relatively large, at around 70 functions. Additionally, some of these functions are specific to the current GC.
We would like to emphasize that the API is NOT stable and is subject to change. We will be working on improving this API and reducing the surface area. This will be future work and we're not working on it in this phase.
Future plans
gc_impl.c
.