Skip to content

perf(resource): demangle each callee once instead of once per rule - #263

Merged
SizzleUnrlsd merged 3 commits into
mainfrom
perf/resource-rule-names
Nov 7, 2026
Merged

SizzleUnrlsd merged 3 commits into
mainfrom
perf/resource-rule-names

Conversation

@SizzleUnrlsd

Copy link
Copy Markdown
Contributor

Summary

ruleMatchesFunction compares a resource-model rule with three forms of a callee's name: the mangled name, the
demangled name, and the base name (the demangled name cut at ().

  • The problem: it demangled the name on every call, once per rule of the model, at every iteration. On one chunk
    of the self-analysis, profiled in Release on macOS, about half of the samples were under ruleMatchesFunction,
    most of them in __cxa_demangle.
  • The change: the two demangled forms are now cached per mangled name. The comparisons themselves are unchanged.
    The cache is:
    • one llvm::StringMap per thread (thread_local), since the analysis threads share nothing, so it needs no lock;
    • keyed on the mangled name, of which both forms are a pure function, so an entry stays valid after the module that
      held the callee is freed;
    • cleared when it reaches 65,536 names, which only bounds its memory.

Tests

testResourceRuleMatchesFunction (11bdafe, ca191e5) pins which names a rule is compared with:

  • what it covers: exact and glob matches on each of the three names, and negative cases;
  • it runs twice, each time in a fresh module;
  • it passes on the code before and after this change.

Seven mutants of ruleMatchesFunction, each dropping one way a rule may match, all make it fail.

  • ca191e5 adds the case that catches dropping the exact match on the full demangled name.
  • Without it, that mutant survived: the other full-signature patterns hold a *, so they take the glob path.

Verification

Setup:

  • Linux x86_64, Ubuntu 24.04 in Docker (4 CPUs, 10 GiB), LLVM 20.1.8.
  • Built Release with CI's configuration: GCC 13.3, -O3 -DNDEBUG, lld.
  • Base: main at 703da23.

Tests: the unit tests and run_test.py (2467/2467) pass on four builds: main, this branch at 7cd302e, #241,
and 7cd302e merged with #241 (the merge has no conflict).

  • ca191e5 only adds a unit-test case. That case passes on this branch's code.
  • The analyzer binary is the same at 7cd302e and ca191e5, byte for byte.

Diagnostics: CI's self-analysis command (run_code_analysis.py, profile fast, generic resource model, summaries
in memory only), run on main's own sources and on zlib, tinyxml2 and PCRE2.

Timing

Protocol: the self-analysis of main's sources with CI's command (4 analyzer processes). After one warm-up pass
each, the two binaries were alternated A B B A four times: 8 passes per arm. Real time is measured against an
NTP-synchronized clock.

Per pass main This branch Change
Real time 63.9 s (62.5 to 65.3) 51.5 s (50.7 to 52.8) −19.4 %
CPU time 236.4 s 187.8 s −20.6 %
Largest process 2518 MiB 2518 MiB +0.0 %
Container memory, at most 8.74 GiB 8.75 GiB +0.1 %
  • Consistency: each A B B A block gives between −19.0 and −20.4 % of real time.
  • The corpora show no change in single runs: zlib and tinyxml2 take a few seconds, and PCRE2's 16 minutes do not
    come from demangling.

Related: #199. This change does not touch the sequential multi-file loop described there.

ruleMatchesFunction demangled the callee's name on every call, so once per
rule of the model, at every fixpoint iteration and every pass. It now reads
the demangled name and its base from a per-thread cache keyed on the mangled
name, of which both are a pure function: no synchronization is needed, and
an entry stays valid after the module that held the callee is freed. The
cache is cleared when it holds 65536 names, which only bounds its memory.

The names a pattern is compared with, and how, are unchanged.
…haracter

The two full-signature patterns of testResourceRuleMatchesFunction,
"ns::Handle::open(char const*)" and "acquire_buf(void**)", both hold a '*', so
they take the glob path: the exact comparison with the full demangled name was
never exercised, and dropping it left the test green.

"ns::Handle::close()" holds no glob character and only equals the full
demangled name. It passes on the code before and after the demangling cache,
and fails, in both rounds, once that comparison is dropped.
@SizzleUnrlsd
SizzleUnrlsd marked this pull request as ready for review November 6, 2026 14:24
@SizzleUnrlsd
SizzleUnrlsd merged commit b1228c6 into main Nov 7, 2026
15 checks passed
@SizzleUnrlsd
SizzleUnrlsd deleted the perf/resource-rule-names branch November 7, 2026 15:19
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