Skip to content

Instantly share code, notes, and snippets.

@jhawthorn
Last active September 10, 2019 17:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jhawthorn/e5921e6aad37d454af958d82f2bc2f94 to your computer and use it in GitHub Desktop.
Save jhawthorn/e5921e6aad37d454af958d82f2bc2f94 to your computer and use it in GitHub Desktop.
Script to benchmark the initial request after a fork. Records stats and makes a flamegraph
REQUEST_URI = ARGV[0] || "http://0.0.0.0/"
REQUEST_OPTS = {
'REMOTE_ADDR' => "127.0.0.1",
'HTTP_HOST' => '0.0.0.0'
}
ARGV.clear
require "actionview_precompiler"
require "flamegraph"
require "./config/environment"
APP = Rack::Server.new.app
def run_in_fork
3.times { GC.start }
rd, wr = IO.pipe
pid = fork do
rd.close
3.times { GC.start }
result = yield
rescue Exception => exception
puts exception
puts exception.backtrace
ensure
wr.write(Marshal.dump(exception: exception, result: result))
wr.close
Process.exit!(0)
end
Process.waitpid(pid)
wr.close
ret = Marshal.load(rd.read)
raise ret[:exception] if ret[:exception]
ret[:result]
end
def time_render
time = Benchmark.ms do
request_env = Rack::MockRequest.env_for(REQUEST_URI, REQUEST_OPTS)
status, env, body = APP.call(request_env)
body.each(&:to_s)
body.close if body.respond_to?(:close)
end
end
def run_flamegraph(name)
run_in_fork do
filename = "tmp/flamegraph_#{name}.html"
Flamegraph.generate(filename) do
time_render
end
nil
end
end
def measure(iterations = 100)
results = []
iterations.times do
results << run_in_fork do
time_render
end
end
results
end
def print_stats(results, name)
File.open("results.csv", "a") do |f|
results.each do |result|
f.puts [name, result].join(",")
end
end
results = results.sort
n = results.size
p(
avg: results.sum / n,
p75: results[n * 75 / 100],
p95: results[n * 95 / 100],
p99: results[n * 99 / 100]
)
end
iter = 100
File.write("results.csv", "name,time\n")
# Warm any caches outside the process (ex. memcache)
run_in_fork do
time_render
end
run_in_fork do
puts "cold"
run_flamegraph("cold")
results = measure(iter)
print_stats(results, "cold")
end
run_in_fork do
puts "precompiled"
ActionviewPrecompiler.precompile
run_flamegraph("precompiled")
results = measure(iter)
print_stats(results, "precompiled")
end
run_in_fork do
puts "warmed"
time_render
run_flamegraph("warmed")
results = measure(iter)
print_stats(results, "warmed")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment