Improving Rails Performance | Part 2 | Memory Benchmarking

Juan Carlos Garzon
iCapital Network Technology Group
3 min readFeb 16, 2018

There comes a time in the course of development, when we realize how poorly optimized our code is. Ideally, we aim to do this as we code, but as we know, that’s not always the case. The pressure to get things done can and does often win over getting things done optimally.

Tasked with that mission, I decided to document my efforts, and just as importantly MEASURE performance, and share it with the developer community.

All my examples and tools are coming from Rails 4.2.7.1 + Angular 1.x perspective. But most of the general ideas are platform agnostic, and useful regardless of tech stack.

Benchmarking

So what is Benchmarking? Put simply benchmarking is the process gathering and comparing performance metrics.

In software development, there are two metrics in particular that we pay attention too. Those are execution time and memory. These are so important that there is a mathematical notation that is used in computer science to classify and analyze algorithms. If you don’t know it, I suggest you brush up on Big O notation, understanding these concepts should be your first line of defense toward writing code that performs faster and consumes less memory. I won’t go into here, but I may touch on this here or there.

Simple guide to Memory Profiling

Install the profiler

In Part 1, we performed execution time benchmarking. The other factor in benchmarking is memory consumption. However, unlike execution time, in Ruby memory consumption requires a gem to be installed. You can get that by adding gem ‘memory_profiler’ to your gemfile, or installing it globally with gem install memory_profiler’ in your terminal.

Then just require the profiler in the class your testing.
require ‘memory_profiler’

Set your $stdout

To make your life easier, set your $stdout. Doing this will log your results to a log file of your choosing, and will make it that much easier for you see your results.

$stdout = File.new(‘log/mem_perf.log’, ‘a+’)
$stdout.sync = true

Keep in mind any `puts`, or `print` command will be logged in this file.

Now Benchmark your code

For memory profiling, you can call this block multiple times, but you won’t want too; you’ll see why when we view the results.

report = MemoryProfiler.report do
users = User.all
# Insert code here
end

View the results

Unlike the Benchmark module, which outputs the result automagically. The Memory Profiler needs to be explicitly told to write the result. Luckily Memory Profiler provides a helper method for that.

report.pretty_print

With the code in place, and $stout set. The only thing left to do is run your code.

From there all need to do is open log/mem_perf.log (if you used my example) and see your results. What you will see will be very intimidating, its long and throws a lot of information at you. Here is where you get the most benefit out of setting the $stout. Don’t get bogged down by all the info, for basic memory benchmarking purposes, just pay attention to the first two lines.

Total allocated: 6856 bytes (127 objects)
Total retained: 2512 bytes (31 objects)
allocated memory by gem
— — — — — — — — — — — — — — — — — -
6496 activerecord-4.2.7.1
160 awesome_print-1.7.0
120 arel-6.0.4
80 acts_as_paranoid-0.5.0
allocated memory by file
— — — — — — — — — — — — — — — — — -
1800 /Users/juangarzon/.rvm/gems/ruby-2.3.5/gems/activerecord-4.2.7.1/lib/active_record/relation.rb
1512 /Users/juangarzon/.rvm/gems/ruby-2.3.5/gems/activerecord-4.2.7.1/lib/active_record/scoping.rb
1200 /Users/juangarzon/.rvm/gems/ruby-2.3.5/gems/activerecord-4.2.7.1/lib/active_record/relation/query_methods.rb
512 /Users/juangarzon/.rvm/gems/ruby-2.3.5/gems/activerecord-4.2.7.1/lib/active_record/relation/spawn_methods.rb
480 /Users/juangarzon/.rvm/gems/ruby-2.3.5/gems/activerecord-4.2.7.1/lib/active_record/relation/merger.rb
etc… etc…

The report breaks down 2 key concepts. Total allocated, and Total retained.

Retained: Is long-lived memory use and object count retained due to the execution of the code block.
Allocated: Is all object allocation and memory allocation during code block.

Here is where you have to use your own judgment, and experience to determine how much is too much memory usage. I like to see how much memory is used when I’m eager loading heavy models in Rails.

For more information about these tools. Visit the memory profiler github page.

--

--