Optimizing Rails

Track problem

Use some kind of monitoring solution to track server perfomance. You can choose from something more general, like Nagios, NewRlic or Stackdriver, to something specifilly tailored for Rails like Skylight or Scout.

Narrow down source of a problem

N+1 problem

Easy to fix when you know you have one. Use Skylight or Bullet to detect it. You also can discover it from rack-mini-profiler or RailsPanel.

Slow queries

Use the index, Luke. Use Skylight or DBs slow queries log to detect a problem.

Generally slow

Use profiler to detect why is your application is slow. Use stackprof in :wall mode or rack-mini-profiler with ?pp=flamegraph.

stackprof

require 'stackprof'
require File.expand_path('../../config/environment',  __FILE__)
exit unless Rails.env.development?
sp = (ENV['STACKPROF'] || :wall).to_sym
StackProf.start(mode: sp, raw: true)
# code which we want to test
StackProf.stop
StackProf.results(File.join(Dir.pwd, "tmp/stackprof-#{sp}.dump"))
stackprof tmp/stackprof-wall.dump --text

rack-mini-profiler

# For memory profiling (requires Ruby MRI 2.1+)
gem 'memory_profiler'

# For call-stack profiling flamegraphs (requires Ruby MRI 2.0.0+)
gem 'flamegraph'
gem 'stackprof'     # For Ruby MRI 2.1+
gem 'rack-mini-profiler', require: false

config/initializers/rack_profiler.rb

if Rails.env == 'development'
  require 'rack-mini-profiler'

  # initialization is skipped so trigger it
  Rack::MiniProfilerRails.initialize!(Rails.application)

  # set RedisStore
  uri = URI.parse(ENV.fetch("REDIS_URI"))
  Rack::MiniProfiler.config.storage_options = { :host => uri.host, :port => uri.port, :password => uri.password }
  Rack::MiniProfiler.config.storage = Rack::MiniProfiler::RedisStore
end

Consumes a lot of memory

Use profiler to detect why is your application is slow. Use stackprof in :object moder or rack-mini-profiler with ?pp=analyze-memory or memory_profiler.

Also check Derailed Benchmarks: bundle exec derailed bundle:mem.

Also gc_tracer, allocation_tracer, allocation_stats and built-in ObjectSpace can help.

config.ru

require ::File.expand_path('../config/environment', __FILE__)

require 'rack/gc_tracer'
require 'rack/allocation_tracer'

use Rack::GCTracerMiddleware, view_page_path: '/gc_tracer', filename: '/tmp/rails-gc_tracer'
use Rack::AllocationTracerMiddleware

run Rails.application

Check how GC works

Use rack-mini-profiler with ?pp=profile-gc, gc_tracer or built-in GC::Profiler.

To read:

Hunting memory errors outside of Ruby object space

Use ruby with jemalloc and jemal. Use this command to check if ruby compiled with jemalloc: ruby -r rbconfig -e "puts RbConfig::CONFIG['LIBS']".

To read:

if ENV['JEMALLOC_STATS']
  STDERR.puts "JEMALLOC_STATS enabled"

  require 'jemal'

  if Jemal.jemalloc_builtin?
    STDERR.puts "jemalloc found"
    Thread.new do
      first = true

      while true
        sleep 5
        stats = Jemal.stats
        stats[:ts] = Time.now.utc.to_i

        File.open(Rails.root + 'log/jemalloc.log', first ? 'w' : 'a') do |f|
          f.puts stats.to_json
        end

        first = false
      end
    end
  end
end

Other tools

About memory

Benchmark

Use built-in ruby module Benchmark and benchmark-memory.

require "benchmark"
Benchmark.benchmark(Benchmark::CAPTION, 7, Benchmark::FORMAT) do |x|
  x.report("test:"){ ypur_code }
end

Faster gems

Faster than default implementations:

gem "escape_utils"
gem "fast_blank"
gem "oj"
gem "faster_path"
gem "ox"
gem "hamlit"
gem "mini_mime"
gem "fast_gettext"

Faster boot

Super fast ruby

Projects That Are Making Blazing Fast Ruby a Reality, 2016. Polyglot From The Very Old To The Very New.

Native Extensions