Skip to content

Instantly share code, notes, and snippets.

@SamSaffron
Last active April 29, 2020 07:10
Show Gist options
  • Save SamSaffron/88512ff1ede0e438da395800c79cbb08 to your computer and use it in GitHub Desktop.
Save SamSaffron/88512ff1ede0e438da395800c79cbb08 to your computer and use it in GitHub Desktop.
require 'benchmark/ips'
require 'redis'
class MethodProfiler
def self.patch(klass, methods, name)
patches = methods.map do |method_name|
<<~RUBY
unless defined?(#{method_name}__mp_unpatched)
alias_method :#{method_name}__mp_unpatched, :#{method_name}
def #{method_name}(*args, &blk)
unless prof = Thread.current[:_method_profiler]
return #{method_name}__mp_unpatched(*args, &blk)
end
begin
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
#{method_name}__mp_unpatched(*args, &blk)
ensure
data = (prof[:#{name}] ||= {duration: 0.0, calls: 0})
data[:duration] += Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
data[:calls] += 1
end
end
end
RUBY
end.join("\n")
klass.class_eval patches
end
def self.start
Thread.current[:_method_profiler] = {
__start: Process.clock_gettime(Process::CLOCK_MONOTONIC)
}
end
def self.stop
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
if data = Thread.current[:_method_profiler]
Thread.current[:_method_profiler] = nil
start = data.delete(:__start)
data[:total_duration] = finish - start
data
end
end
end
$redis = Redis.new
class Test
def work
#$redis.get 'X'
end
def method
work
end
def method_unpatched
work
end
end
MethodProfiler.patch(Test, [:method], :a_thing)
MethodProfiler.start
t = Test.new
Benchmark.ips do |b|
b.report "method" do |times|
i = 0
while i < times
t.method
i += 1
end
end
b.report "method_unpatched" do |times|
i = 0
while i < times
t.method_unpatched
i += 1
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment