Skip to content

Commit a68016f

Browse files
ci(perf): update ddtrace microbenchmarks to cover the full OpenTelemetry Tracing API (#13536)
## Summary Extend ddtrace microbenchmarks to test the following scenarios: - `otel_span`: Update the benchmark to test the full OTel Tracing API by testing these additional Span methods: - `Span.add_event` - `Span.add_link` - `Span.get_span_context` - `Span.is_recording` - `Span.record_exception` - `Span.set_status` - `Span.update_name` - `span`: Update the benchmarks to test ddtrace APIs that match the full OTel Tracing API mentioned above - `otel_sdk_span`: Add a new benchmark that _does not run ddtrace_ and instead runs opentelemetry-sdk, so that we can compare the performance of ddtrace (`otel_span` benchmark) against opentelemetry-sdk ## Notes for reviewers - The `otel_span` and `span` benchmarks are not currently executing the span link methods because they're not publicly available. This should be addressed in a follow-up PR - Since the `otel_sdk_span` doesn't run ddtrace, should we actually test this on every commit? I'd suggest running this on some recurring basis so we can accurately determine where we introduce overhead as compared to the OpenTelemetry SDK, but it may be wasteful to run this on every commit. WDYT? I'm happy to revise this as requested. - The additional scenarios increase the runtime of the `otel_span` and the `span` benchmarks in CI from ~3 minutes to >5 minutes. ## 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)
1 parent 07de3a4 commit a68016f

File tree

9 files changed

+275
-1
lines changed

9 files changed

+275
-1
lines changed

.gitlab/benchmarks/microbenchmarks.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ microbenchmarks:
145145
- "flask_sqli"
146146
- "core_api"
147147
- "otel_span"
148+
- "otel_sdk_span"
148149
- "appsec_iast_aspects"
149150
- "appsec_iast_aspects_ospath"
150151
- "appsec_iast_aspects_re_module"
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Scenarios should be consistent with dd-trace-py/benchmarks/span/config.yml
2+
start: &base
3+
nspans: 1000
4+
ntags: 0
5+
ltags: 0
6+
nmetrics: 0
7+
finishspan: false
8+
telemetry: false
9+
add_event: false
10+
add_link: false
11+
get_context: false
12+
is_recording: false
13+
record_exception: false
14+
set_status: false
15+
update_name: false
16+
add-tags:
17+
<<: *base
18+
ntags: 100
19+
ltags: 100
20+
add-metrics:
21+
<<: *base
22+
nmetrics: 100
23+
add-event:
24+
<<: *base
25+
add_event: true
26+
add-link:
27+
<<: *base
28+
add_link: true
29+
get-context:
30+
<<: *base
31+
get_context: true
32+
is-recording:
33+
<<: *base
34+
is_recording: true
35+
record-exception:
36+
<<: *base
37+
record_exception: true
38+
set-status:
39+
<<: *base
40+
set_status: true
41+
update-name:
42+
<<: *base
43+
update_name: true
44+
start-finish:
45+
<<: *base
46+
finishspan: true
47+
start-finish-telemetry:
48+
<<: *base
49+
finishspan: true
50+
telemetry: true
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
opentelemetry-api==1.24.0
2+
opentelemetry-sdk==1.24.0
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import bm
2+
import bm.utils as utils
3+
from opentelemetry.sdk.trace import TracerProvider
4+
from opentelemetry.trace import SpanContext
5+
from opentelemetry.trace import get_tracer
6+
from opentelemetry.trace import set_tracer_provider
7+
from opentelemetry.trace.status import Status as OtelStatus
8+
from opentelemetry.trace.status import StatusCode as OtelStatusCode
9+
10+
11+
set_tracer_provider(TracerProvider())
12+
otel_tracer = get_tracer(__name__)
13+
14+
15+
class OtelSdkSpan(bm.Scenario):
16+
nspans: int
17+
ntags: int
18+
ltags: int
19+
nmetrics: int
20+
finishspan: bool
21+
telemetry: bool
22+
add_event: bool
23+
add_link: bool
24+
get_context: bool
25+
is_recording: bool
26+
record_exception: bool
27+
set_status: bool
28+
update_name: bool
29+
30+
def run(self):
31+
# run scenario to also set tags on spans
32+
tags = utils.gen_tags(self)
33+
settags = len(tags) > 0
34+
35+
# run scenario to also set metrics on spans
36+
metrics = utils.gen_metrics(self)
37+
setmetrics = len(metrics) > 0
38+
39+
# run scenario to include finishing spans
40+
# Note - if finishspan is False the span will be gc'd when the SpanAggregrator._traces is reset
41+
# (ex: tracer.configure(filter) is called)
42+
finishspan = self.finishspan
43+
# config._telemetry_enabled = self.telemetry
44+
add_event = self.add_event
45+
add_link = self.add_link
46+
get_context = self.get_context
47+
is_recording = self.is_recording
48+
record_exception = self.record_exception
49+
set_status = self.set_status
50+
update_name = self.update_name
51+
52+
# Pre-allocate all of the unique strings we'll need, that way the baseline memory overhead
53+
# is held constant throughout all tests.
54+
test_attributes = {"key": "value"}
55+
test_exception = RuntimeError("test_exception")
56+
test_link_context = SpanContext(trace_id=1, span_id=2, is_remote=False)
57+
test_status = OtelStatus(OtelStatusCode.ERROR, "something went wrong")
58+
59+
def _(loops):
60+
for _ in range(loops):
61+
for i in range(self.nspans):
62+
s = otel_tracer.start_span("test." + str(i))
63+
64+
if settags:
65+
s.set_attributes(tags)
66+
if setmetrics:
67+
s.set_attributes(metrics)
68+
if add_event:
69+
s.add_event("test.event", test_attributes)
70+
if add_link:
71+
s.add_link(test_link_context)
72+
if get_context:
73+
_ = s.get_span_context()
74+
if is_recording:
75+
_ = s.is_recording()
76+
if record_exception:
77+
s.record_exception(test_exception)
78+
if set_status:
79+
s.set_status(test_status)
80+
if update_name:
81+
s.update_name("test.renamed." + str(i))
82+
if finishspan:
83+
s.end()
84+
85+
yield _

benchmarks/otel_span/config.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,42 @@ start: &base
66
nmetrics: 0
77
finishspan: false
88
telemetry: false
9+
add_event: false
10+
add_link: false
11+
get_context: false
12+
is_recording: false
13+
record_exception: false
14+
set_status: false
15+
update_name: false
916
add-tags:
1017
<<: *base
1118
ntags: 100
1219
ltags: 100
1320
add-metrics:
1421
<<: *base
1522
nmetrics: 100
23+
add-event:
24+
<<: *base
25+
add_event: true
26+
# Run add-link scenario when the API is implemented in the Datadog OTel Tracing API
27+
# add-link:
28+
# <<: *base
29+
# add_link: true
30+
get-context:
31+
<<: *base
32+
get_context: true
33+
is-recording:
34+
<<: *base
35+
is_recording: true
36+
record-exception:
37+
<<: *base
38+
record_exception: true
39+
set-status:
40+
<<: *base
41+
set_status: true
42+
update-name:
43+
<<: *base
44+
update_name: true
1645
start-finish:
1746
<<: *base
1847
finishspan: true
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
opentelemetry-api==1.18.0
1+
opentelemetry-api==1.24.0

benchmarks/otel_span/scenario.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import bm
44
import bm.utils as utils
5+
from opentelemetry.trace import SpanContext
56
from opentelemetry.trace import get_tracer
67
from opentelemetry.trace import set_tracer_provider
8+
from opentelemetry.trace.status import Status as OtelStatus
9+
from opentelemetry.trace.status import StatusCode as OtelStatusCode
710

811
from ddtrace import config
912
from ddtrace.opentelemetry import TracerProvider # Requires ``ddtrace>=1.11``
@@ -22,6 +25,13 @@ class OtelSpan(bm.Scenario):
2225
nmetrics: int
2326
finishspan: bool
2427
telemetry: bool
28+
add_event: bool
29+
add_link: bool
30+
get_context: bool
31+
is_recording: bool
32+
record_exception: bool
33+
set_status: bool
34+
update_name: bool
2535

2636
def run(self):
2737
# run scenario to also set tags on spans
@@ -37,18 +47,48 @@ def run(self):
3747
# (ex: tracer.configure(filter) is called)
3848
finishspan = self.finishspan
3949
config._telemetry_enabled = self.telemetry
50+
add_event = self.add_event
51+
add_link = self.add_link
52+
get_context = self.get_context
53+
is_recording = self.is_recording
54+
record_exception = self.record_exception
55+
set_status = self.set_status
56+
update_name = self.update_name
57+
4058
# Recreate span processors and configure global tracer to avoid sending traces to the agent
4159
utils.drop_traces(tracer)
4260
utils.drop_telemetry_events()
4361

62+
# Pre-allocate all of the unique strings we'll need, that way the baseline memory overhead
63+
# is held constant throughout all tests.
64+
test_attributes = {"key": "value"}
65+
test_exception = RuntimeError("test_exception")
66+
test_link_context = SpanContext(trace_id=1, span_id=2, is_remote=False)
67+
test_status = OtelStatus(OtelStatusCode.ERROR, "something went wrong")
68+
4469
def _(loops):
4570
for _ in range(loops):
4671
for i in range(self.nspans):
4772
s = otel_tracer.start_span("test." + str(i))
73+
4874
if settags:
4975
s.set_attributes(tags)
5076
if setmetrics:
5177
s.set_attributes(metrics)
78+
if add_event:
79+
s.add_event("test.event", test_attributes)
80+
if add_link:
81+
s.add_link(test_link_context)
82+
if get_context:
83+
_ = s.get_span_context()
84+
if is_recording:
85+
_ = s.is_recording()
86+
if record_exception:
87+
s.record_exception(test_exception)
88+
if set_status:
89+
s.set_status(test_status)
90+
if update_name:
91+
s.update_name("test.renamed." + str(i))
5292
if finishspan:
5393
s.end()
5494

benchmarks/span/config.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ start: &base
66
finishspan: false
77
traceid128: false
88
telemetry: false
9+
add_event: false
10+
add_link: false
11+
get_context: false
12+
is_recording: false
13+
record_exception: false
14+
set_status: false
15+
update_name: false
916
start-traceid128:
1017
<<: *base
1118
traceid128: true
@@ -16,6 +23,28 @@ add-tags:
1623
add-metrics:
1724
<<: *base
1825
nmetrics: 100
26+
add-event:
27+
<<: *base
28+
add_event: true
29+
# Run add-link scenario when the API is implemented in the Datadog Tracing API
30+
# add-link:
31+
# <<: *base
32+
# add_link: true
33+
get-context:
34+
<<: *base
35+
get_context: true
36+
is-recording:
37+
<<: *base
38+
is_recording: true
39+
record-exception:
40+
<<: *base
41+
record_exception: true
42+
set-status:
43+
<<: *base
44+
set_status: true
45+
update-name:
46+
<<: *base
47+
update_name: true
1948
start-finish:
2049
<<: *base
2150
finishspan: true

benchmarks/span/scenario.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import bm.utils as utils
33

44
from ddtrace import config
5+
from ddtrace.constants import ERROR_MSG
56
from ddtrace.trace import tracer
67

78

@@ -13,6 +14,13 @@ class Span(Scenario):
1314
finishspan: bool
1415
traceid128: bool
1516
telemetry: bool
17+
add_event: bool
18+
add_link: bool
19+
get_context: bool
20+
is_recording: bool
21+
record_exception: bool
22+
set_status: bool
23+
update_name: bool
1624

1725
def run(self):
1826
# run scenario to also set tags on spans
@@ -29,10 +37,23 @@ def run(self):
2937
finishspan = self.finishspan
3038
config._128_bit_trace_id_enabled = self.traceid128
3139
config._telemetry_enabled = config._telemetry_metrics_enabled = self.telemetry
40+
add_event = self.add_event
41+
add_link = self.add_link
42+
get_context = self.get_context
43+
is_recording = self.is_recording
44+
record_exception = self.record_exception
45+
set_status = self.set_status
46+
update_name = self.update_name
47+
3248
# Recreate span processors and configure global tracer to avoid sending traces to the agent
3349
utils.drop_traces(tracer)
3450
utils.drop_telemetry_events()
3551

52+
# Pre-allocate all of the unique strings we'll need, that way the baseline memory overhead
53+
# is held constant throughout all tests.
54+
test_attributes = {"key": "value"}
55+
test_exception = RuntimeError("test_exception")
56+
3657
def _(loops):
3758
for _ in range(loops):
3859
for i in range(self.nspans):
@@ -41,6 +62,23 @@ def _(loops):
4162
s.set_tags(tags)
4263
if setmetrics:
4364
s.set_metrics(metrics)
65+
if add_event:
66+
s._add_event("test.event", test_attributes)
67+
if add_link:
68+
s.set_link(trace_id=1, span_id=2)
69+
if get_context:
70+
_ = s.context
71+
if is_recording:
72+
_ = not s.finished
73+
if record_exception:
74+
s.record_exception(test_exception)
75+
if set_status:
76+
# Perform the equivalent operation for
77+
# test_status = OtelStatus(OtelStatusCode.ERROR, "something went wrong")
78+
s.error = 1
79+
s.set_tag(ERROR_MSG, "something went wrong")
80+
if update_name:
81+
s.resource = "test.renamed." + str(i)
4482
if finishspan:
4583
s.finish()
4684

0 commit comments

Comments
 (0)