Loading…
Optimizing Ruby’s Memory Layout: Variable Width Allocation
2023-10-18
- Source
- Shopify
- Published
- Added to Yomu
Summary
CRuby’s original garbage collector stores most Ruby objects in fixed 40-byte RVALUE slots, leaving differently sized data partly unused or forcing additional allocations through malloc. Shopify’s Variable Width Allocation project adds dynamic-sized allocation APIs and five fixed-size pools—40, 80, 160, 320, and 640 bytes—while increasing heap pages from 16 KB to 64 KB. The design targets cache locality, malloc overhead, memory utilization, and future garbage-collector experimentation; a new GC.stat_heap API reports size-pool statistics, and an escape hatch can disable the feature at build time on Ruby 3.2. Benchmarks on development Ruby 3.2.0 showed gains in most workloads, including 9.8% for psych-load, 5.3% for hexapdf, and 2.1% for railsbench, while mail was 1.7% slower.
Context
CRuby’s single 40-byte RVALUE allocation size simplifies garbage-collector management but can waste space, reduce cache utilization, require external malloc allocations, and limit experimentation with alternative garbage collectors.
Approach / What changed
Variable Width Allocation introduces APIs for dynamically sized objects and fixed-size garbage-collector pools of 40, 80, 160, 320, and 640 bytes. The work also increases heap page size from 16 KB to 64 KB, adds GC.stat_heap statistics, and provides a Ruby 3.2 build-time escape hatch for disabling the feature.
Takeaways
- Ruby objects that exceed an RVALUE may store data externally through malloc, adding allocation overhead and potentially requiring pointers or allocator metadata.
- The size pools use powers-of-two multiples of the 40-byte RVALUE, a choice measured to provide approximately 75% average slot utilization and reduce frequent resizing.
- The benchmark suite used Ruby 3.2.0 development commit 80e56d1 with YJIT disabled; mail was the only listed workload slower with Variable Width Allocation enabled.