Skip to content

feat(profiling): prototype heap-live shadow bitmap#4041

Draft
realFlowControl wants to merge 3 commits into
florian/no-jira-heap-live-tracking-benchmarkfrom
florian/no-jira-heap-live-prefix-poc
Draft

feat(profiling): prototype heap-live shadow bitmap#4041
realFlowControl wants to merge 3 commits into
florian/no-jira-heap-live-tracking-benchmarkfrom
florian/no-jira-heap-live-prefix-poc

Conversation

@realFlowControl

@realFlowControl realFlowControl commented Jul 15, 2026

Copy link
Copy Markdown
Member

Note

this was my clanker trying out things, I have not looked at it yet, just wanted CI to run.

Description

This draft builds on #4045 and adds a thread-local sparse shadow bitmap to the production LocalLiveHeapTracker. ZendMM's eight-byte-aligned pointer address is split into a four-kilobyte application-page number and an exact slot bit; a six-level radix tree lazily maps the page number to a 64-byte bitmap covering all 512 possible allocation starts on that page.

A bit is set only after a sampled allocation is successfully inserted into the existing live-heap DashMap. Free and successful realloc test and clear that exact bit first: a clear bit skips the map lookup, while a set bit removes the full LiveHeapSample. The DashMap remains the source of stack, size, label, and profile metadata.

Pointers pass through unchanged. This removes the allocation prefix, alignment overhead, clean-epoch state, startup and late-thread ZendMM resets, TSRM singleton callback, and virtual-CWD lifecycle handling. Pre-hook allocations, neighboring custom allocators, USE_ZEND_ALLOC=0, and every supported PHP version use the same pointer-preserving path.

The shadow map lives inside the existing explicitly initialized and destroyed allocation-profiling TLS. Radix nodes use Rust's system allocator rather than ZendMM, avoiding allocator-hook recursion. Empty radix leaves are deliberately retained until thread teardown; pruning can be added if long-lived heaps show unbounded metadata growth.

The PR also documents allocation-footer and ZendMM chunk-map alternatives.

Benchmark

Criterion calls the production LocalLiveHeapTracker::track and untrack methods with 2,048 existing 32-byte samples. It times batches of 256 calls so fixture mutation and per-operation timer overhead are excluded. Results on an Apple M4 Max:

Path DashMap only Shadow bitmap Change
Allocate tracked 5.484–5.504 ns 6.864–6.893 ns +24.7%
Free tracked 5.512–5.539 ns 7.059–7.098 ns +28.4%
Free untracked 2.634–2.651 ns 1.506–1.514 ns -43.1%

The measured bookkeeping breaks even at roughly three untracked frees per tracked allocation/free lifecycle; allocation sampling makes the expected ratio substantially higher.

Validation

  • Profiler Rust unit tests: 23 passed, 0 failed.
  • Shadow bitmap and ext-parallel PHPTs passed on PHP 8.5 ZTS amd64.
  • Profiler clippy passed with the unrelated pre-existing got_macho.rs lint suppressed.
  • cargo fmt --check and git diff --check passed.

Reviewer checklist

  • Test coverage seems ok.
  • Appropriate labels assigned.

@realFlowControl realFlowControl added the profiling Relates to the Continuous Profiler label Jul 15, 2026
@datadog-prod-us1-6

datadog-prod-us1-6 Bot commented Jul 15, 2026

Copy link
Copy Markdown

Pipelines  Tests

Unblock PR with BitsAI

⚠️ Warnings

🚦 16 Pipeline jobs failed

DataDog/apm-reliability/dd-trace-php | clippy NTS: [7.1]   View in Datadog   GitLab

DataDog/apm-reliability/dd-trace-php | clippy NTS: [7.2]   View in Datadog   GitLab

DataDog/apm-reliability/dd-trace-php | clippy NTS: [7.3]   View in Datadog   GitLab

View all 16 failed jobs.

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

🔄 Datadog auto-retried 1 job - 0 passed on retry View in Datadog

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 54.11% (+0.04%)

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 6abe5ab | Docs | Datadog PR Page | Give us feedback!

@pr-commenter

pr-commenter Bot commented Jul 15, 2026

Copy link
Copy Markdown

Benchmarks [ profiler ]

Benchmark execution time: 2026-07-17 11:21:19

Comparing candidate commit 6abe5ab in PR branch florian/no-jira-heap-live-prefix-poc with baseline commit 9a0cf90 in branch florian/no-jira-heap-live-tracking-benchmark.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 27 metrics, 9 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

@realFlowControl realFlowControl changed the title feat(profiling): prototype heap-live allocation prefixes feat(profiling): prototype heap-live shadow bitmap Jul 17, 2026
@realFlowControl
realFlowControl force-pushed the florian/no-jira-heap-live-prefix-poc branch from 5333a02 to d22dbe0 Compare July 17, 2026 10:56
@realFlowControl
realFlowControl changed the base branch from master to florian/no-jira-heap-live-tracking-benchmark July 17, 2026 10:57
@realFlowControl
realFlowControl force-pushed the florian/no-jira-heap-live-prefix-poc branch from d22dbe0 to 83df4a2 Compare July 17, 2026 10:57
@realFlowControl
realFlowControl force-pushed the florian/no-jira-heap-live-prefix-poc branch from 83df4a2 to 6abe5ab Compare July 17, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

profiling Relates to the Continuous Profiler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant