Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master

Freeze string literals when not mutated.

I wrote a utility that helps find areas where you could optimize your program using a frozen string instead of a string literal, it's called [let_it_go](https://github.com/schneems/let_it_go). After going through the output and adding `.freeze` I was able to eliminate the creation of 1,114 string objects on EVERY request to [codetriage](codetriage.com). How does this impact execution?

To look at memory:

```ruby
require 'get_process_mem'

mem = GetProcessMem.new
GC.start
GC.disable
1_114.times { " " }
before = mem.mb

after = mem.mb
GC.enable
puts "Diff: #{after - before} mb"

```

Creating 1,114 string objects results in `Diff: 0.03125 mb` of RAM allocated on every request. Or 1mb every 32 requests.

To look at raw speed:

```ruby
require 'benchmark/ips'

number_of_objects_reduced = 1_114

Benchmark.ips do |x|
  x.report("freeze")    { number_of_objects_reduced.times { " ".freeze } }
  x.report("no-freeze") { number_of_objects_reduced.times { " " } }
end
```

We get the results

```
Calculating -------------------------------------
              freeze     1.428k i/100ms
           no-freeze   609.000  i/100ms
-------------------------------------------------
              freeze     14.363k (± 8.5%) i/s -     71.400k
           no-freeze      6.084k (± 8.1%) i/s -     30.450k
```

Now we can do some maths:

```ruby
ips = 6_226k # iterations / 1 second
call_time_before = 1.0 / ips # seconds per iteration 

ips = 15_254 # iterations / 1 second
call_time_after = 1.0 / ips # seconds per iteration 

diff = call_time_before - call_time_after

number_of_objects_reduced * diff * 100

# => 0.4530373333993266 miliseconds saved per request
```

So we're shaving off 1 second of execution time for every 220 requests. 

Is this going to be an insane speed boost to any Rails app: nope. Should we merge it: yep. 

p.s. If you know of a method call that doesn't modify a string input such as [String#gsub](https://github.com/schneems/let_it_go/blob/b0e2da69f0cca87ab581022baa43291cdf48638c/lib/let_it_go/core_ext/string.rb#L37) please [give me a pull request to the appropriate file](https://github.com/schneems/let_it_go/blob/b0e2da69f0cca87ab581022baa43291cdf48638c/lib/let_it_go/core_ext/string.rb#L37), or open an issue in LetItGo so we can track and freeze more strings. 

Keep those strings Frozen

![](https://www.dropbox.com/s/z4dj9fdsv213r4v/let-it-go.gif?dl=1)
latest commit 5bb1d4d288
@schneems schneems authored
..
Failed to load latest commit information.
locale Move the `validate!` method to `ActiveModel::Validations`.
serializers Use Active Model, not ActiveModel in plain English
validations Freeze string literals when not mutated.
attribute_assignment.rb Follow-up to #10776
attribute_methods.rb Freeze string literals when not mutated.
callbacks.rb Fix description for AM::Callbacks
conversion.rb Use Active Model, not ActiveModel in plain English
dirty.rb minor rdoc syntax fix [ci skip]
errors.rb formatting changes
forbidden_attributes_protection.rb Check attributes passed to create_with and where
gem_version.rb Use Active Model, not ActiveModel in plain English
lint.rb Better docs for AM::Lint::Tests
model.rb use attribute assignment module logic during active model initialization
naming.rb Freeze string literals when not mutated.
railtie.rb Use BCrypt's MIN_COST in the test environment for speedier tests
secure_password.rb Fix spelling error in has_secure_password documentation [ci skip]
serialization.rb Revert "Add code example for include option of AM::Serialization#seri…
test_case.rb Remove dead code from AMo.
translation.rb Convert ActiveModel to 1.9 hash syntax.
validations.rb Revert "Revert "Reduce allocations when running AR callbacks.""
validator.rb Change the deprecation messages to show the preferred way to work with
version.rb Use Active Model, not ActiveModel in plain English
Something went wrong with that request. Please try again.