Skip to content

Commit df7393f

Browse files
chore(profiling): add recursive test to benchmarking suite (#13628)
New benchmarking scenario to measure the performance impact of different DD_PROFILING_STACK_V2_MAX_FRAMES values on applications with deep call stacks. For context, DD_PROFILING_STACK_V2_MAX_FRAMES controls the maximum number of stack frames captured and sampled by the profiler. We want to test the performance overhead of different stack frame upper limits (by running a recursive function to create the stack frames) and we are planning to increase this limit from 64 to 512 frames to provide users with more complete stack traces. The configurable parameters are - max_depth: controls recursion depth (stack frame count) - enable_sleep/sleep_duration: mixed CPU/idle workload patterns - profiler_enabled: enable profiler for frame limit testing ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) --------- Co-authored-by: Taegyun Kim <taegyun.kim@datadoghq.com>
1 parent 0e795ab commit df7393f

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

.gitlab/benchmarks/microbenchmarks.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ microbenchmarks:
162162
- "rate_limiter"
163163
- "packages_package_for_root_module_mapping"
164164
- "packages_update_imported_dependencies"
165+
- "recursive_computation"
165166
- "telemetry_add_metric"
166167
- "startup"
167168

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
shallow: &base
2+
max_depth: 10
3+
enable_sleep: false
4+
sleep_duration: 0.1
5+
profiler_enabled: false
6+
7+
medium:
8+
<<: *base
9+
max_depth: 50
10+
11+
deep:
12+
<<: *base
13+
max_depth: 400
14+
15+
deep-profiled:
16+
<<: *base
17+
max_depth: 400
18+
profiler_enabled: true
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import time
2+
from typing import Callable
3+
from typing import Generator
4+
5+
import bm
6+
import bm.utils as utils
7+
8+
from ddtrace.trace import tracer
9+
10+
11+
class RecursiveComputation(bm.Scenario):
12+
name: str
13+
max_depth: int
14+
enable_sleep: bool
15+
sleep_duration: float
16+
profiler_enabled: bool
17+
18+
def cpu_intensive_computation(self, depth: int) -> int:
19+
limit = 100 + (depth * 10)
20+
primes = []
21+
22+
for num in range(2, limit):
23+
is_prime = True
24+
for i in range(2, int(num**0.5) + 1):
25+
if num % i == 0:
26+
is_prime = False
27+
break
28+
29+
if is_prime:
30+
primes.append(num)
31+
32+
return len(primes)
33+
34+
def recursive_traced_computation(self, depth: int = 0) -> int:
35+
with tracer.trace(f"recursive_computation.depth_{depth}") as span:
36+
span.set_tag("recursion.depth", depth)
37+
span.set_tag("recursion.max_depth", self.max_depth)
38+
span.set_tag("profiler.enabled", self.profiler_enabled)
39+
span.set_tag("component", "recursive_computation")
40+
41+
if depth % 3 == 0:
42+
start_time = time.time()
43+
result = self.cpu_intensive_computation(depth)
44+
compute_time = time.time() - start_time
45+
46+
span.set_metric("computation.time_ms", compute_time * 1000)
47+
span.set_metric("computation.result", result)
48+
else:
49+
result = depth
50+
span.set_metric("computation.time_ms", 0)
51+
span.set_metric("computation.result", result)
52+
53+
if depth < self.max_depth:
54+
child_result = self.recursive_traced_computation(depth + 1)
55+
span.set_metric("child.result", child_result)
56+
result += child_result
57+
elif self.enable_sleep:
58+
span.set_tag("action", "sleep_at_max_depth")
59+
time.sleep(self.sleep_duration)
60+
61+
span.set_metric("final.result", result)
62+
return result
63+
64+
def run(self) -> Generator[Callable[[int], None], None, None]:
65+
if self.profiler_enabled:
66+
import ddtrace.profiling.auto # noqa: F401
67+
68+
utils.drop_traces(tracer)
69+
utils.drop_telemetry_events()
70+
71+
def _(loops: int) -> None:
72+
for _ in range(loops):
73+
self.recursive_traced_computation()
74+
75+
yield _

0 commit comments

Comments
 (0)