Skip to content

Commit 7808575

Browse files
authored
Merge pull request #3989 from DataDog/ivoanjo/prof-10589-gvl-profiling-microbenchmark
[PROF-10589] Add microbenchmark for GVL profiling sampling
2 parents 58b17f0 + 5585532 commit 7808575

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed

benchmarks/profiler_sample_gvl.rb

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Used to quickly run benchmark under RSpec as part of the usual test suite, to validate it didn't bitrot
2+
VALIDATE_BENCHMARK_MODE = ENV['VALIDATE_BENCHMARK'] == 'true'
3+
4+
return unless __FILE__ == $PROGRAM_NAME || VALIDATE_BENCHMARK_MODE
5+
6+
require_relative 'benchmarks_helper'
7+
8+
if RUBY_VERSION < '3.2'
9+
if VALIDATE_BENCHMARK_MODE
10+
# To simplify things, we allow this benchmark to be run in VALIDATE_BENCHMARK_MODE even though it's a no-op
11+
$stderr.puts "Skipping benchmark because it requires Ruby 3.2 or newer"
12+
return
13+
else
14+
raise 'This benchmark requires Ruby 3.2 or newer'
15+
end
16+
end
17+
18+
# This benchmark measures the performance of the main stack sampling loop of the profiler
19+
20+
class ProfilerSampleGvlBenchmark
21+
# This is needed because we're directly invoking the collector through a testing interface; in normal
22+
# use a profiler thread is automatically used.
23+
PROFILER_OVERHEAD_STACK_THREAD = Thread.new { sleep }
24+
25+
def initialize
26+
create_profiler
27+
@target_thread = thread_with_very_deep_stack
28+
29+
# Sample once to trigger thread context creation for all threads (including @target_thread)
30+
Datadog::Profiling::Collectors::ThreadContext::Testing._native_sample(@collector, PROFILER_OVERHEAD_STACK_THREAD)
31+
end
32+
33+
def create_profiler
34+
@recorder = Datadog::Profiling::StackRecorder.new(
35+
cpu_time_enabled: true,
36+
alloc_samples_enabled: false,
37+
heap_samples_enabled: false,
38+
heap_size_enabled: false,
39+
heap_sample_every: 1,
40+
timeline_enabled: true,
41+
)
42+
@collector = Datadog::Profiling::Collectors::ThreadContext.for_testing(
43+
recorder: @recorder,
44+
waiting_for_gvl_threshold_ns: 0,
45+
timeline_enabled: true,
46+
)
47+
end
48+
49+
def thread_with_very_deep_stack(depth: 200)
50+
deep_stack = proc do |n|
51+
if n > 0
52+
deep_stack.call(n - 1)
53+
else
54+
sleep
55+
end
56+
end
57+
58+
Thread.new { deep_stack.call(depth) }.tap { |t| t.name = "Deep stack #{depth}" }
59+
end
60+
61+
def run_benchmark
62+
Benchmark.ips do |x|
63+
benchmark_time = VALIDATE_BENCHMARK_MODE ? { time: 0.01, warmup: 0 } : { time: 20, warmup: 2 }
64+
x.config(
65+
**benchmark_time,
66+
)
67+
68+
x.report("gvl benchmark samples") do
69+
Datadog::Profiling::Collectors::ThreadContext::Testing._native_on_gvl_waiting(@target_thread)
70+
Datadog::Profiling::Collectors::ThreadContext::Testing._native_on_gvl_running(@target_thread)
71+
Datadog::Profiling::Collectors::ThreadContext::Testing._native_sample_after_gvl_running(@collector, @target_thread)
72+
end
73+
74+
x.save! "#{File.basename(__FILE__)}-results.json" unless VALIDATE_BENCHMARK_MODE
75+
x.compare!
76+
end
77+
78+
@recorder.serialize!
79+
end
80+
end
81+
82+
puts "Current pid is #{Process.pid}"
83+
84+
ProfilerSampleGvlBenchmark.new.instance_exec do
85+
run_benchmark
86+
end

benchmarks/run_all.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ for file in \
1414
`dirname "$0"`/profiler_memory_sample_serialize.rb \
1515
`dirname "$0"`/profiler_sample_loop_v2.rb \
1616
`dirname "$0"`/profiler_sample_serialize.rb \
17+
`dirname "$0"`/profiler_sample_gvl.rb \
1718
`dirname "$0"`/tracing_trace.rb;
1819
do
1920
bundle exec ruby "$file"

spec/datadog/profiling/validate_benchmarks_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"profiler_memory_sample_serialize",
1818
"profiler_sample_loop_v2",
1919
"profiler_sample_serialize",
20+
"profiler_sample_gvl",
2021
].freeze
2122

2223
benchmarks_to_validate.each do |benchmark|

0 commit comments

Comments
 (0)