Skip to content

perf(mallocfree): only poison freed V blocks at --sanity-level=3 - #31

Open
codspeed-hq[bot] wants to merge 1 commit into
masterfrom
codspeed-optim-skip-the-0xdd-free-block-poison-fill-in-vg-arena-f-1785559563247
Open

perf(mallocfree): only poison freed V blocks at --sanity-level=3#31
codspeed-hq[bot] wants to merge 1 commit into
masterfrom
codspeed-optim-skip-the-0xdd-free-block-poison-fill-in-vg-arena-f-1785559563247

Conversation

@codspeed-hq

@codspeed-hq codspeed-hq Bot commented Aug 1, 2026

Copy link
Copy Markdown

Summary

VG_(arena_free) overwrites every byte of every block Valgrind frees with 0xDD. This is purely a debugging aid — it only makes use-after-free bugs inside V itself more likely to be noticed — but it costs real time on translation/debuginfo-heavy runs: the fill writes the whole block, including the parts the owner never used (e.g. an over-allocated XArray), so hundreds of MB of stores are issued per run just to be thrown away.

This gates the fill behind --sanity-level=3, the threshold already used in this codebase for expensive self-checks (m_signals.c, m_scheduler, aspacemgr). It mirrors the decision already made a few hundred lines above for the malloc-side fill, which upstream disables by default (if (0 && aid != VG_AR_CLIENT)) for the same reason.

if (aid != VG_AR_CLIENT && UNLIKELY(VG_(clo_sanity_level) >= 3))
   VG_(memset)(ptr, 0xDD, (SizeT)b_pszB);

Client memory (VG_AR_CLIENT) was already excluded from the fill, so nothing about how the client's heap is treated changes. Core developers can still get the poison fill with --sanity-level=3 (and it is implicitly on at the level 4 that gdbserver monitor commands raise it to).

Correctness

Verified against a baseline build of this branch's parent commit, both built from the same tree with the same configure flags:

  • Callgrind output is byte-for-byte identical (full callgrind.out, including the summary: event counts) on echo, once the pid line and the install prefix baked into cob= are normalized.
  • All 22 Callgrind regression tests pass (callgrind/tests, the same set CI runs).
  • Memcheck suite: 292 tests run, with the identical 14 pre-existing environment-related failures on both the baseline and the patched build (amd64/insn_*, gone_abrt_xml, sem, vcpu_bz2 — they fail the same way without this change in this container, so they are not caused by it).

Measured impact

Measured locally with codspeed run -m walltime on the exec-harness benchmarks (base and head runs interleaved twice, 25 rounds each, config generated with bench/generate_config.py):

Benchmark Base → Head
echo Hello, World!, no-inline 205.9 ms → 202.6 ms
echo Hello, World!, inline 284.5 ms → 282.9 ms
python3 test.py, inline 1.53 s → 1.51 s
llsc_tzconvert_bench 5000, no-inline 260.4 ms → 257.2 ms

Head was faster than or equal to base on every benchmark in every one of the four runs, and a 30-pair alternating CPU-time A/B on echo, no-inline had head faster in 25/30 pairs (median −1.5%).

Caveat, stated plainly: the gain observed in this sandbox is ~1–1.5%, smaller than on the bare-metal codspeed-macro runners where this was originally profiled (there the win came mostly from kernel page-zeroing on first touch of the poisoned pages; in this virtualized container the minor-fault count is unchanged between the two builds, so only the raw store traffic is saved). The CodSpeed check on this PR runs on the macro runners and will record the real impact.

Scope

One condition and a comment in coregrind/m_mallocfree.c. No option, build, or output changes.

VG_(arena_free) filled every byte of every block V frees with 0xDD. It
is a pure debugging aid for use-after-free bugs inside V itself, but it
writes hundreds of MB per debuginfo-heavy run, including the parts of a
block the owner never touched.

Gate it behind --sanity-level=3, the threshold already used for
expensive self-checks elsewhere in the codebase. This mirrors the
malloc-side fill, which is already disabled by default.

Callgrind output is byte-identical and the Callgrind regression suite
passes unchanged.
@codspeed-hq

codspeed-hq Bot commented Aug 1, 2026

Copy link
Copy Markdown
Author

Merging this PR will not alter performance

✅ 84 untouched benchmarks
⏩ 60 skipped benchmarks1


Comparing codspeed-optim-skip-the-0xdd-free-block-poison-fill-in-vg-arena-f-1785559563247 (af710b0) with master (ae6bf15)

Open in CodSpeed

Footnotes

  1. 60 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@codspeed-hq
codspeed-hq Bot marked this pull request as ready for review August 1, 2026 06:05
@greptile-apps

greptile-apps Bot commented Aug 1, 2026

Copy link
Copy Markdown

Greptile Summary

This PR reduces allocator overhead by limiting internal freed-block poisoning to sanity level 3 or above.

  • Preserves poisoning for expensive diagnostic runs.
  • Leaves client-heap handling unchanged.
  • Aligns the free-side debugging fill with existing sanity-level conventions.

Confidence Score: 5/5

The PR appears safe to merge with no actionable correctness or security issues identified.

The changed condition only suppresses an optional internal payload poison at ordinary sanity levels; allocator checks and free-list operations rely on metadata and redzones rather than the poison bytes.

Important Files Changed

Filename Overview
coregrind/m_mallocfree.c Gates the non-client arena free poison fill behind sanity level 3 without changing allocator metadata handling or client memory behavior.

Reviews (1): Last reviewed commit: "perf(mallocfree): only poison freed V bl..." | Re-trigger Greptile

@codspeed-hq
codspeed-hq Bot requested a review from not-matthias August 1, 2026 06:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant