Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ You can find several test harnesses in this repository:
* harness-perf - a simplified harness that runs for exactly the hinted number of iterations
* harness-bips - a harness that measures iterations/second until stable
* harness-continuous - a harness that adjusts the batch sizes of iterations to run in stable iteration size batches
* harness-cstats - count C method calls and C loop iterations

To use it, run a benchmark script directly, specifying a harness directory with `-I`:

Expand Down
60 changes: 60 additions & 0 deletions harness-cstats/harness.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require_relative '../harness/harness'

# Using Module#prepend to enable TracePoint right before #run_benchmark
# while also reusing the original implementation.
self.singleton_class.prepend Module.new {
def run_benchmark(*)
frames = []
c_calls = Hash.new { 0 }
c_loops = Hash.new { 0 }

method_trace = TracePoint.new(:call, :c_call, :return, :c_return) do |tp|
# Keep track of call frames to get the caller of :b_call
case tp.event
when :call, :c_call
method_name = "#{tp.defined_class}##{tp.method_id}"
frames.push([tp.event, method_name])
when :return, :c_return
frames.pop
end

# Count C method calls
if tp.event == :c_call
c_calls[method_name] += 1
end
end

block_trace = TracePoint.new(:b_call) do |tp|
caller_event, caller_method = frames.last

# Count block calls only when the caller is a C method
if caller_event == :c_call
c_loops[caller_method] += 1
end
end

method_trace.enable
block_trace.enable
super
ensure
block_trace.disable
method_trace.disable

c_loops_total = c_loops.sum(&:last)
c_loops = c_loops.sort_by { |_method, count| -count }.first(100)
c_loops_ratio = 100.0 * c_loops.sum(&:last) / c_loops_total
puts "Top #{c_loops.size} block calls by C methods (#{'%.1f' % c_loops_ratio}% of all #{c_loops_total} calls):"
c_loops.each do |method, count|
puts '%8d (%4.1f%%) %s' % [count, 100.0 * count / c_loops_total, method]
end
puts

c_calls_total = c_calls.sum(&:last)
c_calls = c_calls.sort_by { |_method, count| -count }.first(100)
c_calls_ratio = 100.0 * c_calls.sum(&:last) / c_calls_total
puts "Top #{c_calls.size} C method calls (#{'%.1f' % c_calls_ratio}% of all #{c_calls_total} calls):"
c_calls.sort_by(&:last).reverse.first(100).each do |method, count|
puts '%8d (%4.1f%%) %s' % [count, 100.0 * count / c_calls_total, method]
end
end
}