Faster Apps,
No Memory Thrash
Get Your Ruby Memory Config Right
Noah Gibbs, AppFolio Inc - @codefolio
I’m a Ruby Fellow for Appfolio.
They pay me to do this stuff, and to write at engineering.appfolio.com.
Thank you, Appfolio!
Randomly: Have You Seen the Sendai City Museum? The handmade maps are gorgeous.
きれいですね?
Okay, Now Some Ruby.
Big, Small and Tiny Objects
Tiny Objects
The smallest Ruby objects exist inside their references. No extra allocations.
Small Objects
Small objects live within a 40-byte Slot. 408 Slots are allocated per Page.
Big Objects
Objects that don’t fit in a Slot get allocated one by one. They each also keep a Slot.
Garbage Collection (GC)
GC
Ruby has a generational Mark/Sweep GC. Very good, not best-in-the-world.
Major, Minor
A Major GC checks all objects, while Minor GC checks only newer (“young”) objects.
Manual GC
You can start minor or major GC with an API call:
GC.start # major
GC.start(full_mark: false) # minor
Grow, Collect, then Expand:
The GC Cycle
Phase 1: Grow
Your program creates objects. The objects use Slots and heap memory.
Phase 2: Collect
When you need more memory or have allocated a certain number of bytes, GC starts.
Phase 3: Expand
If there still aren’t enough Slots, allocate more. If we crossed a threshold, raise it.
Over Time
A long-running Ruby app expands to its “natural” size asymptotically.
The Best Way Isn’t Easy
The Problem
Creating more garbage objects makes GC slower, and makes expansion slower.
Best: Cut Waste
“Do nothing” is very fast. More efficient algorithms are good too.
Precalculate
Can you save results somehow? Caching is the next-fastest after not doing anything at all.
Fewer, Bigger
It’s often better to use a single bigger object than several smaller ones.
Destructive Ops
Destructive operations like gsub! and concat can save CPU and memory.
What Ruby Tells You About Memory: GC.stat
Data Firehose
GC.stat has everything. And changes a bit between Ruby versions.
Useful Parts
What You Tell Ruby: Environment Variables
Environment Variables
Env Vars: Why?
By setting the initial size (malloc limit, Slots, etc) you can speed up startup.
EnvMem: A Memory Tool
Expansion
Your process expands in phases. Startup can be a bit slow. Can we fix it?
Fast Start
We can start the new process “after” those expansions. Env vars do that.
Cycle Back
Which settings to use? EnvMem gets them from your process’s GC.stat.
Installing EnvMem
gem install env_mem
# In Gemfile
gem “env_mem”
Running EnvMem
# In your app
File.open(“my_file”, “w”) { |f|
f.print GC.stat.inspect }
# Afterward
env_mem my_file > env_script.sh
Does It Work?
Last year, I showed that CRuby has about 5%-7% warmup time for Rails Ruby Bench.
Does It Work?
With EnvMem there’s no measurable warmup. Just noise.
A Quick Win
Easy Answers
This is hard. Is there anything very simple and faster?
Easy Answers
This is hard. Is there anything very simple and faster?
Yes.
Allocators
Your OS comes with a memory allocator: malloc. But there are others.
jemalloc
Especially on Linux, you’re better off with jemalloc. Ruby already supports it.
Build with jemalloc
# Configure the Ruby source directly
./configure --with-jemalloc
# Or use rvm instead
rvm install 2.5.0 -C --with-jemalloc
# Or rbenv / ruby-build
RUBY_CONFIGURE_OPTS="--with-jemalloc" rbenv install 2.5.0
How Much Faster?
How Much Faster?
I measure about 10%-12% end-to-end total speedup. Some of that is probably better caching because jemalloc uses less memory.
Solutions and Speedups
What Helps?
Advanced Methods
For Debugging
Slot Fragmentation
Full Slots, Stuck
When a Slot is used, it can’t move again until the whole page of 408 Slots is freed.
Check Fragmentation
s = GC.stat
used_ratio = s[:heap_live_slots].to_f / (s[:heap_eden_pages] * 408)
fragmentation = 1.0 - used_ratio
What Ruby Tells You: GC::Profiler
GC Profile Mode
Want more detail about specific garbage collections as they happen? GC::Profiler.enable
GC Profiling
GC::Profiler.enable
# run code that creates garbage (A)
puts GC::Profiler.report
# prints a table of GCs that happened
# during the code in (A)
Source Code:
Noah Gibbs, AppFolio - Tw: @codefolio
These slides: http://bit.ly/kaigi2018-gibbs
Questions?
Noah Gibbs, AppFolio - Tw: @codefolio
These slides: http://bit.ly/kaigi2018-gibbs