Skip to content

Cache ExplicitCallGraph.ExplicitNode.hashCode - #2048

Merged
liblit merged 1 commit into
wala:masterfrom
liblit:perf/cache-ExplicitNode-hashCode
Aug 26, 2026
Merged

Cache ExplicitCallGraph.ExplicitNode.hashCode#2048
liblit merged 1 commit into
wala:masterfrom
liblit:perf/cache-ExplicitNode-hashCode

Conversation

@liblit

@liblit liblit commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

The old implementation recomputed getMethod().hashCode() * 8681 + getContext().hashCode() on every call. However, CGNode objects are canonical per (method, context) pair, both fields are final and never change, and equals() is identity-based. Therefore, caching the hash in a final field is safe and avoids repeated (and re-derived) hash computations. Every node is created through findOrCreateNode, which rejects null methods, so eager computation in the constructor cannot NPE.

Effect on JMH benchmarks (JDK 17; 3 forks × 6 one-second iterations per configuration after warm-up; comparisons against master using per-fork means as the unit of analysis, Welch's t-test, one-sided in the improvement direction):

  • DataflowBenchmark.analyzeTestContextInsensitive
    • baseline: 0.2934 ms/op (99% CI [0.2792, 0.3077])
    • patched: 0.2158 ms/op (99% CI [0.2027, 0.2288])
    • 26.5% faster, p = 0.005
  • DataflowBenchmark.analyzeTestContextSensitive
    • baseline: 0.2233 ms/op (99% CI [0.2145, 0.2321])
    • patched: 0.2136 ms/op (99% CI [0.1993, 0.2278])
    • 4.4% faster, p = 0.046

No other benchmark showed a statistically significant difference in either direction.

A note on multiple comparisons: we ran 19 hypothesis tests per branch, each at the nominal alpha = 0.05 level. Even if this change had no real effect anywhere, uncorrected testing would give roughly a 62% chance of at least one spurious "significant" result (1 - 0.95^19), so raw p-values overstate our confidence. Applying a Holm step-down correction across those 19 tests, the context-insensitive improvement has an adjusted p ("q") of 0.19: above the conventional 0.05 cutoff, though its large ~27% effect size and disjoint 99% confidence intervals leave little practical doubt that it is real. The context-sensitive result does not survive any correction and should be treated as suggestive rather than conclusive.

These gains are consistent with async-profiler data showing ExplicitNode.hashCode as a significant fraction of dataflow-analysis CPU; the same hash chain also feeds LocalPointerKey.hashCode during pointer analysis.

No API change; backward compatible.

The old implementation recomputed `getMethod().hashCode()` * 8681 +
`getContext().hashCode()` on every call.  However, `CGNode` objects
are canonical per (method, context) pair, both fields are `final` and
never change, and `equals()` is identity-based.  Therefore, caching
the hash in a `final` field is safe and avoids repeated (and
re-derived) hash computations.  Every node is created through
`findOrCreateNode`, which rejects `null` methods, so eager computation
in the constructor cannot NPE.

Effect on JMH benchmarks (JDK 17; 3 forks x 6 one-second iterations
per configuration after warm-up; comparisons against `master` using
per-fork means as the unit of analysis, Welch's t-test, one-sided in
the improvement direction):

* `DataflowBenchmark.analyzeTestContextInsensitive`
  * baseline: 0.2934 ms/op (99% CI [0.2792, 0.3077])
  * patched:  0.2158 ms/op (99% CI [0.2027, 0.2288])
  * 26.5% faster, p = 0.005
* `DataflowBenchmark.analyzeTestContextSensitive`
  * baseline: 0.2233 ms/op (99% CI [0.2145, 0.2321])
  * patched:  0.2136 ms/op (99% CI [0.1993, 0.2278])
  * 4.4% faster, p = 0.046

No other benchmark showed a statistically significant difference in
either direction.

A note on multiple comparisons: we ran 19 hypothesis tests per branch,
each at the nominal alpha = 0.05 level.  Even if this change had no
real effect anywhere, uncorrected testing would give roughly a 62%
chance of at least one spurious "significant" result (1 - 0.95^19), so
raw p-values overstate our confidence.  Applying a Holm step-down
correction across those 19 tests, the context-insensitive improvement
has an adjusted p ("q") of 0.19:  above the conventional 0.05 cutoff,
though its large ~27% effect size and disjoint 99% confidence
intervals leave little practical doubt that it is real.  The
context-sensitive result does not survive any correction and should be
treated as suggestive rather than conclusive.

These gains are consistent with async-profiler data showing
`ExplicitNode.hashCode` as a significant fraction of dataflow-analysis
CPU; the same hash chain also feeds `LocalPointerKey.hashCode` during
pointer analysis.

No API change; backward compatible.
@liblit
liblit requested a review from msridhar August 26, 2026 15:21
@liblit liblit self-assigned this Aug 26, 2026
@liblit
liblit enabled auto-merge August 26, 2026 15:22
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 50.81%. Comparing base (3762b68) to head (1b2d5de).

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #2048      +/-   ##
============================================
- Coverage     50.81%   50.81%   -0.01%     
  Complexity    12762    12762              
============================================
  Files          1368     1368              
  Lines         83498    83499       +1     
  Branches      14414    14414              
============================================
- Hits          42432    42431       -1     
- Misses        36382    36383       +1     
- Partials       4684     4685       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@msridhar msridhar left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@liblit
liblit added this pull request to the merge queue Aug 26, 2026
Merged via the queue into wala:master with commit 795b56a Aug 26, 2026
11 checks passed
@liblit
liblit deleted the perf/cache-ExplicitNode-hashCode branch August 26, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants