Hey everyone,
Did you realize that Ruby string literals are allocated every time the enclosing code runs, just in case they might later be mutated? I didn't until this year. For example, this code will allocate (and then garbage collect) the "Hello, world!" string [cue Dr. Evil accent] 1 million times:
1_000_000.times do
LOGGER.info("Hello, world!")
end
Ruby 2.1 originally had an innovative--but ugly--feature to make string literals immutable by putting a trailing `f` after the string, as in
LOGGER.info("Hello, world!"f)
1. That got interesting when several Rails core members implemented the optimizations to allocate the frozen string literal just once at parse time and share it across the whole file.
The above change makes
this benchmark run 1.64X faster! (This is partly because the shared literal strings have the same object_id which makes == on equality return in O(1) time--basically instantly.)
2. Charles Nutter undid the ugly `f` syntax with a cleaner variation that is backward-compatible:
LOGGER.info("Hello, world!".freeze)
But still, to get the speed improvement you'd have to write .freeze after every string literal in your code. IMO that's a non-starter. Therefore...
3. We've submitted
this pull request (and matching
Ruby issue) that adds a magic comment, inspired by the Ruby 1.9's # -*- encoding: utf8 -*- comment:
# -*- immutable: string -*-
Put that at the top of all the files in your project and voilà:
same code speedup with nothing but a magic comment. (And the magic comment can be automatically added to your files by this fork of the magic_encoding gem called, naturally,
magic_immutable_string.)
BTW if any of your tests fail because you actually need to mutate a string literal (this is rare) you can address that by calling
String.new('') # or ''.dup
If you'd like to see Ruby run faster (and be more functional), please help lobby to get this pull request accepted. We will all benefit.
-Colin
Colin Kelley, CTO/co-founder, Invoca, (formerly RingRevenue), Inc.