require "net/http"
def start_server
# Remove the X to enable the parameters for tuning.
# These are the default values as of Ruby 2.2.0.
@child = spawn(<<-EOC.split.join(" "))
XRUBY_GC_HEAP_FREE_SLOTS=4096
XRUBY_GC_HEAP_INIT_SLOTS=10000
XRUBY_GC_HEAP_GROWTH_FACTOR=1.8
XRUBY_GC_HEAP_GROWTH_MAX_SLOTS=0
XRUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=2.0
XRUBY_GC_MALLOC_LIMIT=16777216
XRUBY_GC_MALLOC_LIMIT_MAX=33554432
XRUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=1.4
XRUBY_GC_OLDMALLOC_LIMIT=16777216
XRUBY_GC_OLDMALLOC_LIMIT_MAX=134217728
XRUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=1.2
rails server -p3009 > /dev/null
EOC
sleep 0.1 until alive?
end
def alive?
system("curl http://localhost:3009/ &> /dev/null")
end
def stop_server
Process.kill "HUP", server_pid
Process.wait @child
end
def server_pid
`cat tmp/pids/server.pid`.to_i
end
def memory_size_mb
(`ps -o rss= -p #{server_pid}`.to_i * 1024).to_f / 2**20
end
# In /etc/hosts I have api.rails.local set to 127.0.0.1 for
# API testing on any app. Curl freaks out and takes extra
# seconds to do the request to these .local things, so we
# will use Net::HTTP for moar speed.
def do_request
uri = URI("http://api.rails.local:3009/memory_heavy_action")
req = Net::HTTP::Get.new(uri)
# Remove the next line if you don't need HTTP basic authentication.
req.basic_auth("user@example.com", "password")
req["Content-Type"] = "application/json"
Net::HTTP.start("localhost", uri.port) do |http|
http.read_timeout = 60
http.request(req)
end
end
results = []
# You can’t just measure once: memory usage has some variance.
# We will take the mean of 7 runs.
7.times do
start_server
used_mb = nil
(1..30).map do |n|
print "Request #{n}..."
do_request
used_mb = memory_size_mb
puts "#{used_mb} MB"
end
final_mb = used_mb
results << final_mb
puts "Final Memory: #{final_mb} MB"
stop_server
end
mean_final_mb = results.reduce(:+) / results.size
puts "Mean Final Memory: #{mean_final_mb} MB"