Commit df0dc65
authored
perf(profiling): consolidate greenlet switch updates (#19655)
## Description
Consolidates the two native frame updates performed for each greenlet switch into one private `record_greenlet_switch()` call and one `greenlet_info_map` mutex acquisition.
The existing behavior is preserved: the origin frame is always updated, while the running target frame is left unchanged when it is needed for parent-greenlet stack unwinding.
This is an independently mergeable prerequisite extracted from #18724.
## Testing
- `scripts/lint fmt -- ddtrace/profiling/_gevent.py tests/profiling/test_gevent.py tests/profiling/collector/test_stack.py`
- `scripts/lint cformat`
- `scripts/lint profiling-native-check`
- `scripts/lint checks`
- `git diff --check`
- Fresh CPython 3.12 native extension build
- Native linked-parent switch regression covering preserved and updated target frames
- Existing high-cardinality greenlet switch contention regression
### Benchmark
The benchmark isolates the native operation changed by this PR. Each measured run performs 1,000,000 logical greenlet switch updates after 10,000 warm-up updates:
- baseline `e11d640110`: two `update_greenlet_frame()` native calls and two mutex acquisitions per update
- candidate `93702898f8`: one `record_greenlet_switch()` native call and one mutex acquisition per update
Both `_stack` extensions were built in Release mode from the exact commits, then run in the same container pinned to CPU 0 with `--cpuset-cpus=0`.
Results over five runs:
- before: 0.531681 seconds median, runs `[0.528339, 0.531681, 0.532128, 0.534566, 0.529234]`
- after: 0.366206 seconds median, runs `[0.380415, 0.368066, 0.360803, 0.362898, 0.366206]`
- change: approximately 31% faster for the isolated native update
Environment:
- AWS KVM VM, Intel Xeon Platinum 8175M at 2.50 GHz
- 16 vCPUs, 8 cores with 2 threads per core, 61 GiB RAM
- Linux 6.8.0-1055-aws, x86-64
- Docker server 29.5.2, container pinned to one CPU
- CPython 3.12.13, GCC 14.2.0, glibc 2.41
- container image `sha256:ce7c46dbb7f07d352aecd756e19fd7f39550a72d4a13e68d30f02f5448eebd69`
<details>
<summary>Benchmark script</summary>
```python
import statistics
import time
from ddtrace.internal.datadog.profiling import stack
ITERATIONS = 1_000_000
RUNS = 5
ORIGIN_ID = 101
TARGET_ID = 102
stack.track_greenlet(ORIGIN_ID, "origin", False)
stack.track_greenlet(TARGET_ID, "target", False)
if hasattr(stack, "record_greenlet_switch"):
def update():
stack.record_greenlet_switch(ORIGIN_ID, False, TARGET_ID, None, True)
else:
def update():
stack.update_greenlet_frame(ORIGIN_ID, False)
stack.update_greenlet_frame(TARGET_ID, None)
def run_once():
start = time.perf_counter()
for _ in range(ITERATIONS):
update()
return time.perf_counter() - start
for _ in range(10_000):
update()
runs = [run_once() for _ in range(RUNS)]
stack.untrack_greenlet(ORIGIN_ID)
stack.untrack_greenlet(TARGET_ID)
print(f"median={statistics.median(runs):.6f}s runs={runs}")
```
</details>
This is an isolated native-call benchmark, not an end-to-end application throughput claim. The earlier 67% figure was discarded after reproducing with exact baseline and candidate builds because the candidate run had not activated the profiling hook.
## Risks
Low. The changed Python and native APIs are private and ship together. Existing parent-greenlet frame retention remains unchanged.
## Additional Notes
The GIL release around native state mutation is retained intentionally. It was introduced by #14852 in commit `54a3a0ea35` to prevent potential thread-pool deadlocks while waiting for profiler mutexes. This change reduces two such release windows per greenlet switch to one.
No release note is needed because this is an internal performance optimization with no user-facing API or behavior change.
Co-authored-by: taegyun.kim <taegyun.kim@datadoghq.com>1 parent 76b5d40 commit df0dc65
9 files changed
Lines changed: 107 additions & 28 deletions
File tree
- ddtrace
- internal/datadog/profiling/stack
- include
- src
- echion
- test
- profiling
- tests/profiling/collector
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
101 | | - | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
102 | 108 | | |
103 | 109 | | |
104 | 110 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
69 | | - | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
70 | 76 | | |
71 | 77 | | |
72 | 78 | | |
| |||
Lines changed: 5 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
139 | | - | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
140 | 144 | | |
141 | 145 | | |
142 | 146 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
604 | 604 | | |
605 | 605 | | |
606 | 606 | | |
607 | | - | |
| 607 | + | |
608 | 608 | | |
609 | 609 | | |
610 | 610 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
924 | 924 | | |
925 | 925 | | |
926 | 926 | | |
927 | | - | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
928 | 932 | | |
929 | 933 | | |
930 | | - | |
931 | 934 | | |
932 | | - | |
933 | | - | |
934 | | - | |
935 | | - | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
936 | 943 | | |
937 | 944 | | |
938 | 945 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
510 | 510 | | |
511 | 511 | | |
512 | 512 | | |
513 | | - | |
| 513 | + | |
514 | 514 | | |
515 | | - | |
516 | | - | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
517 | 520 | | |
518 | | - | |
| 521 | + | |
519 | 522 | | |
520 | 523 | | |
521 | 524 | | |
522 | | - | |
| 525 | + | |
| 526 | + | |
523 | 527 | | |
524 | 528 | | |
525 | 529 | | |
| |||
1023 | 1027 | | |
1024 | 1028 | | |
1025 | 1029 | | |
1026 | | - | |
| 1030 | + | |
1027 | 1031 | | |
1028 | 1032 | | |
1029 | 1033 | | |
| |||
Lines changed: 42 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
69 | | - | |
70 | | - | |
71 | | - | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
72 | 80 | | |
73 | 81 | | |
74 | 82 | | |
| |||
97 | 105 | | |
98 | 106 | | |
99 | 107 | | |
100 | | - | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
101 | 114 | | |
102 | | - | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
103 | 119 | | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | 120 | | |
111 | 121 | | |
112 | 122 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1099 | 1099 | | |
1100 | 1100 | | |
1101 | 1101 | | |
1102 | | - | |
| 1102 | + | |
1103 | 1103 | | |
1104 | 1104 | | |
1105 | 1105 | | |
| |||
0 commit comments