Performance Optimization and YJIT
49.1 Measure First
benchmark, benchmark-ips stackprof, vernier (3.2.1+), rbspy Sampling CPU profilers (flame graphs)
memory_profiler, derailed_benchmarks Allocations and memory Web request profiling
APM: New Relic, Datadog, Scout, Skylight, AppSignal
require "benchmark/ips" # gem
Benchmark.ips do |x|
x.report("map + select") { (1..1000).map { it * 2 }.select(&:even?) }
x.report("filter_map") { (1..1000).filter_map { it * 2 if (it * 2).even? } }
x.compare!
end49.2 YJIT (and ZJIT)
YJIT typically gives 15–40% speedups on real apps (Rails enables it by default in 7.2+ when available). Ruby 4.0 introduces ZJIT (a new method-based JIT, experimental).
ruby --yjit app.rb # enable YJIT (Shopify's JIT; production-ready since 3.2)
RUBY_YJIT_ENABLE=1 bundle exec puma
ruby --yjit-stats app.rb49.3 Optimization Techniques
Use # frozen_string_literal: true. Avoid creating needless objects in hot loops. Prefer String#<< for building strings.
Use the right data structures (Set for membership, Hash lookups instead of Array#find).
Use built-in methods (implemented in C): sum, tally, each_with_object, filter_map, flat_map.
Memoize expensive computations (@x ||= ..., or defined?(@x) for falsy values).
Avoid method_missing and OpenStruct in hot paths. Define real methods.
Database: eliminate N+1 queries, add indexes, batch with find_each, cache.
Concurrency: threads for I/O, processes/Ractors for CPU work. Tune Puma workers and threads.
Use jemalloc and tune the GC for servers. Upgrade Ruby (each release improves performance).
Native extensions or Rust (magnus, rb-sys) for hot spots.