Remove deep-freezing via IceNine in favor of Ractor#203
Conversation
IceNine is unmaintained and Ractor is maintained by ruby-core
|
@headius Any tips on what we should do for JRuby here? Keep the IceNine dependency for JRuby only? |
|
Supporting the deep-freezing capability of Ractor.make_shareable is not difficult, but there's no alternative Ruby shipping that currently. I did a native implementation of Ractor's freezing APIs for JRuby here: jruby/jruby#9029 This implementation is a light port of the C logic and tries to emulate the same status flags as Ractor in CRuby to avoid repeatedly traversing a known frozen graph. There's another pure-Ruby implementation of Ractors by @eregon here: jruby/jruby#9029 This version implements more of the API but will tend to re-traverse already-frozen graphs due to being pure-Ruby and not able to manipulate the internal state of objects (especially frozen ones). I think the best approach lies somewhere in the middle, where implementations that already can do parallelism primarily just deep freeze graphs and an API shim layer makes it largely feel the same. I also opened a feature request of sorts, to try to formalize deep freezing as a concept separate from Ractor. If we'd have managed to do that before 4.0, you'd just use that API on its own and not even be bothered with Ractor. Alas, I don't get a lot of free time to shepherd it forward. JRuby 10.1 will ship with some form of Ractor.make_shareable, since I suspect others will also want this API, but I still believe that efficient deep-freezing is a feature that should be independent of Ractor. Perhap we need to pull in someone from IceNine to discuss how we can standardize a deep-freezing API that works whether Ractor is available or not? |
|
I think it'd be good to push for https://bugs.ruby-lang.org/issues/21665 again. |
IceNine is unmaintained and Ractor is maintained by ruby-core.
There's an open proposal to add deep_freeze to Ruby core, but it hasn't been accepted yet.
Also, since Ractor is implemented in C, it's faster and more memory efficient than IceNine (which is implemented in Ruby). I show this with benchmarks below.
I worked with Claude to write some speed and memory/allocations benchmarks. Memory is a bit tricky to benchmark, so I provided both RSS (which is inconsistent due to GC timing) and just number of allocations (with GC stopped).
Note that there are GC benefits to freezing (deeply frozen objects get pushed out of the GC passes that are run frequently), they're just hard to benchmark. An additional, if obvious benefit is that it... well, makes the object shareable via Ractor as well.
I used Ractor as the baseline to show that it's faster than IceNine. I also included non-deep freezing ("regular") for comparison.
Note that
Dry::Struct::Values are technically deprecated, but I think we should re-consider that, since it does have benefits.Deep Freeze Benchmark Results
Speed
Speed Benchmark Script
Simple (2 attributes)
Nested (7 attributes, struct + array)
Ractor is ~2.3× faster than IceNine, at ~54–67% the speed of no freezing.
Allocations
Allocations Benchmark Script
Simple (2 attributes)
.newNested (7 attributes, struct + array)
.newRactor allocates 2× fewer objects than IceNine per
.new.Memory (RSS)
Memory (RSS) Benchmark Script
Simple (2 attributes)
Nested (7 attributes, struct + array)
Ractor uses 33–87% less memory than IceNine, within 6–11% of no freezing.
Summary
Ractor-based deep freezing outperforms IceNine across every dimension: