Skip to content

Commit 631d8ba

Browse files
feat(perf): add encoder scenario (#2646)
* feat(performance): add encoder scenario * support buffered encoder * change variants * print comparison table * default run id * use fast option for pyperf * Apply suggestions from code review * Apply suggestions from code review * cleanup * flake8 fix Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 1d6fedf commit 631d8ba

File tree

5 files changed

+150
-0
lines changed

5 files changed

+150
-0
lines changed

benchmarks/base.Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ WORKDIR /app
1414
RUN apt-get update && apt-get install --no-install-recommends -y \
1515
curl \
1616
git \
17+
# ddtrace includes c extensions
18+
build-essential \
19+
# uuid is used to generate identifier for run if one is not provided
20+
uuid-runtime \
1721
# cleaning up unused files
1822
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
1923
&& rm -rf /var/lib/apt/lists/*
@@ -36,6 +40,7 @@ RUN pip install -r requirements.txt
3640

3741
# For performance testing
3842
ENV DDTRACE_GIT_COMMIT_ID ""
43+
ENV RUN_ID ""
3944
ENV PIP_INSTALL_WHEELS ""
4045
ENV SIRUN_NO_STDIO 0
4146

benchmarks/encoder/benchmark

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
set -o errexit
4+
set -o pipefail
5+
set -o nounset
6+
7+
mkdir -p /artifacts/${RUN_ID}
8+
9+
RESULTS_1=/artifacts/${RUN_ID}/${DDTRACE_GIT_COMMIT_ID_1}.json
10+
RESULTS_2=/artifacts/${RUN_ID}/${DDTRACE_GIT_COMMIT_ID_2}.json
11+
12+
# append venvs with ddtrace to sys.path
13+
14+
PYTHONPATH=/app/.venv_1/lib/python3.9/site-packages \
15+
python scenario.py \
16+
--copy-env \
17+
--fast \
18+
-o ${RESULTS_1}
19+
20+
PYTHONPATH=/app/.venv_2/lib/python3.9/site-packages \
21+
python scenario.py \
22+
--copy-env \
23+
--fast \
24+
-o ${RESULTS_2}
25+
26+
pyperf compare_to --table ${RESULTS_2} ${RESULTS_1}

benchmarks/encoder/entrypoint

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/bash
2+
3+
set -o errexit
4+
set -o pipefail
5+
set -o nounset
6+
7+
if [[ -z "${RUN_ID}" ]]; then
8+
RUN_ID=$(uuidgen)
9+
fi
10+
11+
python3 -m venv /app/.venv_1
12+
python3 -m venv /app/.venv_2
13+
14+
source /app/.venv_1/bin/activate
15+
pip install git+https://github.com/Datadog/dd-trace-py@${DDTRACE_GIT_COMMIT_ID_1}
16+
deactivate
17+
18+
source /app/.venv_2/bin/activate
19+
pip install git+https://github.com/Datadog/dd-trace-py@${DDTRACE_GIT_COMMIT_ID_2}
20+
deactivate
21+
22+
exec "$@"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pyperf

benchmarks/encoder/scenario.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import random
2+
import string
3+
4+
import pyperf
5+
6+
from ddtrace.internal.encoding import Encoder
7+
from ddtrace.span import Span
8+
9+
10+
try:
11+
# the introduction of the buffered encoder changed the internal api
12+
# see https://github.com/DataDog/dd-trace-py/pull/2422
13+
from ddtrace.internal._encoding import BufferedEncoder # noqa: F401
14+
15+
def _init_encoder(max_size=8 << 20, max_item_size=8 << 20):
16+
return Encoder(max_size, max_item_size)
17+
18+
19+
except ImportError:
20+
21+
def _init_encoder():
22+
return Encoder()
23+
24+
25+
VARIANTS = [
26+
dict(ntraces=1, nspans=1000),
27+
dict(ntraces=100, nspans=1000),
28+
dict(ntraces=1, nspans=1000, ntags=1, ltags=16),
29+
dict(ntraces=1, nspans=1000, ntags=48, ltags=64),
30+
dict(ntraces=1, nspans=1000, nmetrics=1),
31+
dict(ntraces=1, nspans=1000, nmetrics=48),
32+
]
33+
34+
35+
def _rands(size=6, chars=string.ascii_uppercase + string.digits):
36+
return "".join(random.choice(chars) for _ in range(size))
37+
38+
39+
def _random_values(k, size):
40+
if k == 0:
41+
return []
42+
43+
return list(set([_rands(size=size) for _ in range(k)]))
44+
45+
46+
def _gen_data(ntraces=1, nspans=1, ntags=0, ltags=0, nmetrics=0):
47+
traces = []
48+
49+
# choose from a set of randomly generated span attributes
50+
span_names = _random_values(256, 16)
51+
resources = _random_values(256, 16)
52+
services = _random_values(16, 16)
53+
tag_keys = _random_values(ntags, 16)
54+
metric_keys = _random_values(nmetrics, 16)
55+
56+
for _ in range(ntraces):
57+
trace = []
58+
for i in range(0, nspans):
59+
# first span is root so has no parent otherwise parent is root span
60+
parent_id = trace[0].span_id if i > 0 else None
61+
span_name = random.choice(span_names)
62+
resource = random.choice(resources)
63+
service = random.choice(services)
64+
with Span(None, span_name, resource=resource, service=service, parent_id=parent_id) as span:
65+
if ntags > 0:
66+
span.set_tags(dict(zip(tag_keys, [_rands(size=ltags) for _ in range(ntags)])))
67+
if nmetrics > 0:
68+
span.set_metrics(
69+
dict(
70+
zip(
71+
metric_keys,
72+
[random.randint(0, 2 ** 16) for _ in range(nmetrics)],
73+
)
74+
)
75+
)
76+
trace.append(span)
77+
traces.append(trace)
78+
return traces
79+
80+
81+
def time_encode(loops, variant):
82+
encoder = _init_encoder()
83+
traces = _gen_data(**variant)
84+
range_it = range(loops)
85+
t0 = pyperf.perf_counter()
86+
for _ in range_it:
87+
encoder.encode_traces(traces)
88+
dt = pyperf.perf_counter() - t0
89+
return dt
90+
91+
92+
if __name__ == "__main__":
93+
runner = pyperf.Runner()
94+
for variant in VARIANTS:
95+
name = "|".join(f"{k}:{v}" for (k, v) in variant.items())
96+
runner.bench_time_func("scenario:encoder|" + name, time_encode, variant)

0 commit comments

Comments
 (0)