Skip to content

Commit b6e3f79

Browse files
[python_many_threads] Initial commit (#136)
1 parent 6f77afa commit b6e3f79

5 files changed

Lines changed: 402 additions & 0 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
ARG BASE_IMAGE="prof-python-3.11"
2+
FROM $BASE_IMAGE
3+
4+
COPY ./scenarios/python_many_threads_3.11/main.py \
5+
./scenarios/python_many_threads_3.11/requirements.txt \
6+
/app/
7+
RUN chmod 644 /app/*
8+
9+
WORKDIR /app
10+
11+
RUN pip install -r requirements.txt
12+
13+
ENV EXECUTION_TIME_SEC="30"
14+
15+
ENV DD_PROFILING_ENABLED="true"
16+
17+
# Force reservoir sampling: there are 20 worker threads but at most 5 are
18+
# sampled per cycle, so the inverse-probability wall-time weighting must
19+
# reconstruct the correct totals.
20+
# We could go down to 1 or 2 but it makes the test very flaky. Even with
21+
# 5/20 sampling and 15% margin, the point is sufficiently proven.
22+
ENV _DD_PROFILING_STACK_MAX_THREADS="5"
23+
24+
# Reservoir sampling with two threads results in very noisy wall time for
25+
# many threads. By reducing the interval, we get more samples and less noise.
26+
ENV _DD_PROFILING_STACK_ADAPTIVE_SAMPLING_ENABLED=1
27+
ENV _DD_PROFILING_STACK_ADAPTIVE_SAMPLING_MAX_INTERVAL_US="10000"
28+
29+
CMD python main.py
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# python_many_threads_3.11
2+
3+
Verifies that the stack sampler's reservoir sampling produces correct wall-time
4+
totals when the number of live threads exceeds the per-cycle sampling cap.
5+
6+
The workload spawns 20 identical CPU-bound `worker` threads that all run for the
7+
full duration. `_DD_PROFILING_STACK_MAX_THREADS=2` forces the sampler to sample
8+
only 2 of the ~21 live threads each cycle (Algorithm R reservoir sampling),
9+
scaling each sampled thread's wall time by `n_total / sample_count`.
10+
11+
## Expected behavior
12+
13+
- **wall-time**: the combined wall time attributed to `worker` is ~20 cores
14+
(one core-second per second per worker). Despite subsampling, the
15+
inverse-probability weighting must reconstruct this total. This is the only
16+
scenario that crosses the `max_threads` cap and exercises reservoir sampling.
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
{
2+
"test_name": "python_many_threads",
3+
"stacks": [
4+
{
5+
"profile-type": "wall-time",
6+
"stack-content": [
7+
{
8+
"regular_expression": ".*worker",
9+
"value": 1000000000,
10+
"error_margin": 10,
11+
"labels": [
12+
{
13+
"key": "thread name",
14+
"values": ["worker-0"]
15+
}
16+
]
17+
}
18+
]
19+
},
20+
{
21+
"profile-type": "wall-time",
22+
"stack-content": [
23+
{
24+
"regular_expression": ".*worker",
25+
"value": 1000000000,
26+
"error_margin": 10,
27+
"labels": [
28+
{
29+
"key": "thread name",
30+
"values": ["worker-1"]
31+
}
32+
]
33+
}
34+
]
35+
},
36+
{
37+
"profile-type": "wall-time",
38+
"stack-content": [
39+
{
40+
"regular_expression": ".*worker",
41+
"value": 1000000000,
42+
"error_margin": 10,
43+
"labels": [
44+
{
45+
"key": "thread name",
46+
"values": ["worker-2"]
47+
}
48+
]
49+
}
50+
]
51+
},
52+
{
53+
"profile-type": "wall-time",
54+
"stack-content": [
55+
{
56+
"regular_expression": ".*worker",
57+
"value": 1000000000,
58+
"error_margin": 10,
59+
"labels": [
60+
{
61+
"key": "thread name",
62+
"values": ["worker-3"]
63+
}
64+
]
65+
}
66+
]
67+
},
68+
{
69+
"profile-type": "wall-time",
70+
"stack-content": [
71+
{
72+
"regular_expression": ".*worker",
73+
"value": 1000000000,
74+
"error_margin": 10,
75+
"labels": [
76+
{
77+
"key": "thread name",
78+
"values": ["worker-4"]
79+
}
80+
]
81+
}
82+
]
83+
},
84+
{
85+
"profile-type": "wall-time",
86+
"stack-content": [
87+
{
88+
"regular_expression": ".*worker",
89+
"value": 1000000000,
90+
"error_margin": 10,
91+
"labels": [
92+
{
93+
"key": "thread name",
94+
"values": ["worker-5"]
95+
}
96+
]
97+
}
98+
]
99+
},
100+
{
101+
"profile-type": "wall-time",
102+
"stack-content": [
103+
{
104+
"regular_expression": ".*worker",
105+
"value": 1000000000,
106+
"error_margin": 10,
107+
"labels": [
108+
{
109+
"key": "thread name",
110+
"values": ["worker-6"]
111+
}
112+
]
113+
}
114+
]
115+
},
116+
{
117+
"profile-type": "wall-time",
118+
"stack-content": [
119+
{
120+
"regular_expression": ".*worker",
121+
"value": 1000000000,
122+
"error_margin": 10,
123+
"labels": [
124+
{
125+
"key": "thread name",
126+
"values": ["worker-7"]
127+
}
128+
]
129+
}
130+
]
131+
},
132+
{
133+
"profile-type": "wall-time",
134+
"stack-content": [
135+
{
136+
"regular_expression": ".*worker",
137+
"value": 1000000000,
138+
"error_margin": 10,
139+
"labels": [
140+
{
141+
"key": "thread name",
142+
"values": ["worker-8"]
143+
}
144+
]
145+
}
146+
]
147+
},
148+
{
149+
"profile-type": "wall-time",
150+
"stack-content": [
151+
{
152+
"regular_expression": ".*worker",
153+
"value": 1000000000,
154+
"error_margin": 10,
155+
"labels": [
156+
{
157+
"key": "thread name",
158+
"values": ["worker-9"]
159+
}
160+
]
161+
}
162+
]
163+
},
164+
{
165+
"profile-type": "wall-time",
166+
"stack-content": [
167+
{
168+
"regular_expression": ".*worker",
169+
"value": 1000000000,
170+
"error_margin": 10,
171+
"labels": [
172+
{
173+
"key": "thread name",
174+
"values": ["worker-10"]
175+
}
176+
]
177+
}
178+
]
179+
},
180+
{
181+
"profile-type": "wall-time",
182+
"stack-content": [
183+
{
184+
"regular_expression": ".*worker",
185+
"value": 1000000000,
186+
"error_margin": 10,
187+
"labels": [
188+
{
189+
"key": "thread name",
190+
"values": ["worker-11"]
191+
}
192+
]
193+
}
194+
]
195+
},
196+
{
197+
"profile-type": "wall-time",
198+
"stack-content": [
199+
{
200+
"regular_expression": ".*worker",
201+
"value": 1000000000,
202+
"error_margin": 10,
203+
"labels": [
204+
{
205+
"key": "thread name",
206+
"values": ["worker-12"]
207+
}
208+
]
209+
}
210+
]
211+
},
212+
{
213+
"profile-type": "wall-time",
214+
"stack-content": [
215+
{
216+
"regular_expression": ".*worker",
217+
"value": 1000000000,
218+
"error_margin": 10,
219+
"labels": [
220+
{
221+
"key": "thread name",
222+
"values": ["worker-13"]
223+
}
224+
]
225+
}
226+
]
227+
},
228+
{
229+
"profile-type": "wall-time",
230+
"stack-content": [
231+
{
232+
"regular_expression": ".*worker",
233+
"value": 1000000000,
234+
"error_margin": 10,
235+
"labels": [
236+
{
237+
"key": "thread name",
238+
"values": ["worker-14"]
239+
}
240+
]
241+
}
242+
]
243+
},
244+
{
245+
"profile-type": "wall-time",
246+
"stack-content": [
247+
{
248+
"regular_expression": ".*worker",
249+
"value": 1000000000,
250+
"error_margin": 10,
251+
"labels": [
252+
{
253+
"key": "thread name",
254+
"values": ["worker-15"]
255+
}
256+
]
257+
}
258+
]
259+
},
260+
{
261+
"profile-type": "wall-time",
262+
"stack-content": [
263+
{
264+
"regular_expression": ".*worker",
265+
"value": 1000000000,
266+
"error_margin": 10,
267+
"labels": [
268+
{
269+
"key": "thread name",
270+
"values": ["worker-16"]
271+
}
272+
]
273+
}
274+
]
275+
},
276+
{
277+
"profile-type": "wall-time",
278+
"stack-content": [
279+
{
280+
"regular_expression": ".*worker",
281+
"value": 1000000000,
282+
"error_margin": 10,
283+
"labels": [
284+
{
285+
"key": "thread name",
286+
"values": ["worker-17"]
287+
}
288+
]
289+
}
290+
]
291+
},
292+
{
293+
"profile-type": "wall-time",
294+
"stack-content": [
295+
{
296+
"regular_expression": ".*worker",
297+
"value": 1000000000,
298+
"error_margin": 10,
299+
"labels": [
300+
{
301+
"key": "thread name",
302+
"values": ["worker-18"]
303+
}
304+
]
305+
}
306+
]
307+
},
308+
{
309+
"profile-type": "wall-time",
310+
"stack-content": [
311+
{
312+
"regular_expression": ".*worker",
313+
"value": 1000000000,
314+
"error_margin": 10,
315+
"labels": [
316+
{
317+
"key": "thread name",
318+
"values": ["worker-19"]
319+
}
320+
]
321+
}
322+
]
323+
}
324+
],
325+
"scale_by_duration": true
326+
}

0 commit comments

Comments
 (0)