Does freezing an object add overhead to running it? Memory, CPU cycles etc?
In many languages, "frozen" or immutable objects typically allow for better performance, less GC overhead, and lighter data-structures when running parallel.
But I can imagine, in Ruby (at least the default Matz' ruby runtime), where this freezing is the non-default and added to the language posterior, it makes everything slower.
And I would imagine running a "freeze" loop over several hundreds, (or thousands?) of ob-classes takes some ms as well.
I would think this overhead is negligible, maybe even unmeasurable small. This is purely out of interest.
The gem isn't freezing instances of the objects, but rather the classes themselves. In Ruby (nearly) everything is an object, so for example Array is an object of type Class. Classes are also open by defualt, allowing them to be altered at run time. There's nothing stopping me doing the following in my app:
irb(main):001* class Array
irb(main):002* def hello
irb(main):003* puts "hello"
irb(main):004* end
irb(main):005> end
=> :hello
irb(main):006> [].hello
hello
This sort of monkey patching is used a lot in the ActiveSupport gem to give convenient methods such as:
irb(main):001:0> 3.days.ago
=> Sat, 28 Dec 2024 13:10:24.562785251 GMT +00:00
Integer is extented with the `days` method making date maths very intuative.
This gem freezes the core classes, essentially closing them, so doing the above raises an error:
irb(main):007> require 'refrigerator'
=> true
irb(main):008> Refrigerator.freeze_core
=> nil
irb(main):009* class Array
irb(main):010* def hello
irb(main):011* puts "hello"
irb(main):012* end
irb(main):013> end
(irb):10:in `<class:Array>': can't modify frozen class: Array (FrozenError)
Looking at the code in the gem, all it's doing is calling https://apidock.com/ruby/Object/freeze on all call modules. The frozen flag is an inbuilt part of the language and as far as I'm aware has a performance benefit. In fact the major verison of Ruby will have all string instances frozen by default.
I know that it freezes the classes (which are also objects, indeed). And I saw it does this by reading classnames from a list of textfiles. Both is not fast. The "freeze" isn't that invasive, from what I can see in the c code, it merely flips a flag at the objects metadata table. But I can imagine the reading from files isn't that fast.
And tracking the mutation flags isn't free either. Though I expect not that big. Checking for the state of this flag at every possible mutation is quite some overhead, I'd presume. But no idea how heavy that'd be.
Again, compared to typical boot stuff like Rails' zeitwerk autoload mapping, it's nothing. And in runtime the only penalty will be when something tries to mutate a class obj, which I'd expect happens almost only on boot in most codebases anyway.
Though I know quite some "gems" and even older Rails implementations (mostly around AR) that would monkeypatch in runtime on instantation even. Like when a state-machine gets built on :new or when methods get added based on database-columns and types, every time such an instance is returned from a db response.
I didn't mean to imply you didn't know that stuff, I just tend to write comments from the basics for anyone unfamiliar who may be reading. Yup I would expect it to have an (extremely small) boot time impact. Though I don't think it would have a runtime impact, since surely ruby has to check the frozen flag on mutation whether the flag has been set to true or not? Also by including the gem you preclude mutating the class objects anyway, since it raises an error.
In many languages, "frozen" or immutable objects typically allow for better performance, less GC overhead, and lighter data-structures when running parallel.
But I can imagine, in Ruby (at least the default Matz' ruby runtime), where this freezing is the non-default and added to the language posterior, it makes everything slower. And I would imagine running a "freeze" loop over several hundreds, (or thousands?) of ob-classes takes some ms as well.
I would think this overhead is negligible, maybe even unmeasurable small. This is purely out of interest.