Skip to content

Commit

Permalink
Merge pull request #3989 from DataDog/ivoanjo/prof-10589-gvl-profilin…
Browse files Browse the repository at this point in the history
…g-microbenchmark

[PROF-10589] Add microbenchmark for GVL profiling sampling
  • Loading branch information
ivoanjo authored Oct 11, 2024
2 parents 58b17f0 + 5585532 commit 7808575
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
86 changes: 86 additions & 0 deletions benchmarks/profiler_sample_gvl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Used to quickly run benchmark under RSpec as part of the usual test suite, to validate it didn't bitrot
VALIDATE_BENCHMARK_MODE = ENV['VALIDATE_BENCHMARK'] == 'true'

return unless __FILE__ == $PROGRAM_NAME || VALIDATE_BENCHMARK_MODE

require_relative 'benchmarks_helper'

if RUBY_VERSION < '3.2'
if VALIDATE_BENCHMARK_MODE
# To simplify things, we allow this benchmark to be run in VALIDATE_BENCHMARK_MODE even though it's a no-op
$stderr.puts "Skipping benchmark because it requires Ruby 3.2 or newer"
return
else
raise 'This benchmark requires Ruby 3.2 or newer'
end
end

# This benchmark measures the performance of the main stack sampling loop of the profiler

class ProfilerSampleGvlBenchmark
# This is needed because we're directly invoking the collector through a testing interface; in normal
# use a profiler thread is automatically used.
PROFILER_OVERHEAD_STACK_THREAD = Thread.new { sleep }

def initialize
create_profiler
@target_thread = thread_with_very_deep_stack

# Sample once to trigger thread context creation for all threads (including @target_thread)
Datadog::Profiling::Collectors::ThreadContext::Testing._native_sample(@collector, PROFILER_OVERHEAD_STACK_THREAD)
end

def create_profiler
@recorder = Datadog::Profiling::StackRecorder.new(
cpu_time_enabled: true,
alloc_samples_enabled: false,
heap_samples_enabled: false,
heap_size_enabled: false,
heap_sample_every: 1,
timeline_enabled: true,
)
@collector = Datadog::Profiling::Collectors::ThreadContext.for_testing(
recorder: @recorder,
waiting_for_gvl_threshold_ns: 0,
timeline_enabled: true,
)
end

def thread_with_very_deep_stack(depth: 200)
deep_stack = proc do |n|
if n > 0
deep_stack.call(n - 1)
else
sleep
end
end

Thread.new { deep_stack.call(depth) }.tap { |t| t.name = "Deep stack #{depth}" }
end

def run_benchmark
Benchmark.ips do |x|
benchmark_time = VALIDATE_BENCHMARK_MODE ? { time: 0.01, warmup: 0 } : { time: 20, warmup: 2 }
x.config(
**benchmark_time,
)

x.report("gvl benchmark samples") do
Datadog::Profiling::Collectors::ThreadContext::Testing._native_on_gvl_waiting(@target_thread)
Datadog::Profiling::Collectors::ThreadContext::Testing._native_on_gvl_running(@target_thread)
Datadog::Profiling::Collectors::ThreadContext::Testing._native_sample_after_gvl_running(@collector, @target_thread)
end

x.save! "#{File.basename(__FILE__)}-results.json" unless VALIDATE_BENCHMARK_MODE
x.compare!
end

@recorder.serialize!
end
end

puts "Current pid is #{Process.pid}"

ProfilerSampleGvlBenchmark.new.instance_exec do
run_benchmark
end
1 change: 1 addition & 0 deletions benchmarks/run_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ for file in \
`dirname "$0"`/profiler_memory_sample_serialize.rb \
`dirname "$0"`/profiler_sample_loop_v2.rb \
`dirname "$0"`/profiler_sample_serialize.rb \
`dirname "$0"`/profiler_sample_gvl.rb \
`dirname "$0"`/tracing_trace.rb;
do
bundle exec ruby "$file"
Expand Down
1 change: 1 addition & 0 deletions spec/datadog/profiling/validate_benchmarks_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"profiler_memory_sample_serialize",
"profiler_sample_loop_v2",
"profiler_sample_serialize",
"profiler_sample_gvl",
].freeze

benchmarks_to_validate.each do |benchmark|
Expand Down

0 comments on commit 7808575

Please sign in to comment.