Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions scenarios/python_greenlet_multithreaded_3.11/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ARG BASE_IMAGE="prof-python-3.11"
FROM $BASE_IMAGE

COPY ./scenarios/python_greenlet_multithreaded_3.11/requirements.txt /app/
RUN chmod 644 /app/*

WORKDIR /app

RUN pip install --no-cache-dir -r requirements.txt

COPY ./scenarios/python_greenlet_multithreaded_3.11/main.py /app/

ENV DD_PROFILING_ENABLED=true
ENV DD_TRACE_ENABLED=false
ENV DD_TRACE_DEBUG=false
ENV DD_PROFILING_OUTPUT_PPROF="/app/data/profiles"
ENV EXECUTION_TIME_SEC="10"

CMD ddtrace-run python main.py
102 changes: 102 additions & 0 deletions scenarios/python_greenlet_multithreaded_3.11/expected_profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{
"test_name": "python_greenlet_multithreaded_3.11",
"scale_by_duration": false,
"pprof-regex": "",
"stacks": [
{
"profile-type": "wall-time",
"pprof-regex": "",
"stack-content": [
{
"regular_expression": ".*MathWorker.*work_fibonacci.*",
"percent": 25,
"error_margin": 10,
"labels": [
{
"key": "thread name",
"values": [
"MathWorker"
]
}
]
}
]
},
{
"profile-type": "wall-time",
"pprof-regex": "",
"stack-content": [
{
"regular_expression": ".*MathWorker.*work_prime.*",
"percent": 10,
"error_margin": 10,
"labels": [
{
"key": "thread name",
"values": [
"MathWorker"
]
}
]
}
]
},
{
"profile-type": "wall-time",
"pprof-regex": "",
"stack-content": [
{
"regular_expression": ".*MathWorker.*work_prime;.*genexpr.*",
"percent": 7,
"error_margin": 10,
"labels": [
{
"key": "thread name",
"values": [
"MathWorker"
]
}
]
}
]
},
{
"profile-type": "wall-time",
"pprof-regex": "",
"stack-content": [
{
"regular_expression": ".*UtilWorker.*work_string.*",
"percent": 15,
"error_margin": 10,
"labels": [
{
"key": "thread name",
"values": [
"UtilWorker"
]
}
]
}
]
},
{
"profile-type": "wall-time",
"pprof-regex": "",
"stack-content": [
{
"regular_expression": ".*UtilWorker.*work_list.*",
"percent": 20,
"error_margin": 10,
"labels": [
{
"key": "thread name",
"values": [
"UtilWorker"
]
}
]
}
]
}
]
}
109 changes: 109 additions & 0 deletions scenarios/python_greenlet_multithreaded_3.11/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""Test scenario for greenlet profiling with multiple threads."""

import random
import threading
import time

import greenlet


class MathWorker:
def __init__(self) -> None:
self.end_time = time.monotonic() + 10

self.fib_greenlet = greenlet.greenlet(self.work_fibonacci)
self.prime_greenlet = greenlet.greenlet(self.work_prime)

self.last_fib = 0
self.last_prime = 0

def do_work(self) -> None:
self.fib_greenlet.switch()
print(f"Fibonacci: {self.last_fib}, Prime: {self.last_prime}")

def work_fibonacci(self) -> None:
while True:
# Find the next Fibonacci number
self.last_fib = self.last_fib + 1

a, b = 0, 1
for _ in range(self.last_fib):
a, b = b, a + b

if self.end_time < time.monotonic():
return

self.prime_greenlet.switch()

def work_prime(self) -> None:
while True:
for _ in range(5):
# Find the next prime number after self.last_prime
candidate = self.last_prime + 1
while True:
is_prime = candidate > 1 and all(candidate % i != 0 for i in range(2, int(candidate**0.5) + 1))
if is_prime:
self.last_prime = candidate
break
candidate += 1

if self.end_time < time.monotonic():
return

self.fib_greenlet.switch()


class UtilWorker:
def __init__(self) -> None:
self.end_time = time.monotonic() + 10

self.string_greenlet = greenlet.greenlet(self.work_string)
self.list_greenlet = greenlet.greenlet(self.work_list)

self.string_iterations = 0
self.list_iterations = 0

def do_work(self) -> None:
self.string_greenlet.switch()
print(f"String iterations: {self.string_iterations}, List iterations: {self.list_iterations}")

def work_string(self) -> None:
while True:
result = ""
for _ in range(100):
result += str(random.randint(0, 100)) # noqa: S311

self.string_iterations += 1

if self.end_time < time.monotonic():
return

self.list_greenlet.switch()

def work_list(self) -> None:
while True:
sums = [1]
for _ in range(100):
sums.append(sum(sums))

self.list_iterations += 1

if self.end_time < time.monotonic():
return

self.string_greenlet.switch()


def main() -> None:
t = threading.Thread(target=lambda: MathWorker().do_work(), name="MathWorker")
u = threading.Thread(target=lambda: UtilWorker().do_work(), name="UtilWorker")

t.start()
u.start()

t.join()
u.join()


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ddtrace
greenlet