finalize some performance-critical types - #2049
Conversation
|
Question for reviewers: are we confident that the affected types are not intended to be subtyped/subclassed by non-WALA code? |
|
Interesting! I'm surprised this has such a big effect on some benchmarks in the steady state, once the JIT has warmed up. I assumed it would speculatively devirtualize / inline without making types Do we think that
WALA leaves a lot of types open for subclassing, and I can't guarantee no one has tried to subclass these. The nice thing, though, is that these changes are very easy to revert if we get complaints. |
Speculation guards are costly in two ways: the branch itself and lost opportunities for other optimizations.
No. The JIT compiler does not leverage |
Ok, then, maybe we can remove the (I would have thought |
Agreed. Will do. |
Mark a number of heavily used fixpoint-iteration and `int`-set types
as `final` where subclassing was never intended. This includes
`BitVectorVariable`, `BooleanVariable`, machine states,
points-to-set variables, etc.. Marking these hot types as `final`
gives the JIT more freedom for devirtualization and inlining along
the dataflow/fixpoint fast path. No behavioral change; the only
externally visible effect is that the affected classes can no longer
be subclassed.
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.2150 ms/op (99% CI [0.2029, 0.2272])
* 26.7% faster, p = 0.006
* `SlicerBenchmark.computeBackwardSliceInTestList`
* baseline: 29.53 ms/op (99% CI [28.96, 30.09])
* patched: 28.02 ms/op (99% CI [27.58, 28.46])
* 5.1% faster, p < 0.001
* `AtomBenchmark.concatAtoms`: 1.5% faster (p = 0.002)
* `AtomBenchmark.startsWith`: 0.7% faster (p = 0.017)
The context-sensitive dataflow benchmark also improved by 8.7%, though
that difference did not reach statistical significance with so few
forks (p = 0.088). No other benchmark showed a statistically
significant change 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 slicer improvement
remains clearly significant (adjusted p ("q") = 0.002) and the small
`concatAtoms` win is marginal (q = 0.085). The large dataflow
improvement has q = 0.187, nominally above the conventional 0.05
cutoff because only 3-4 forks limit our statistical power, but its
~27% effect size and disjoint 99% confidence intervals leave little
practical doubt that it is real.
223fafe to
eb92b0c
Compare
seal or finalize some performance-critical typesfinalize some performance-critical types
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2049 +/- ##
============================================
- 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 36384 +2
Partials 4684 4684 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Mark a number of heavily used fixpoint-iteration and
int-set types asfinalwhere subclassing was never intended. This includesBitVectorVariable,BooleanVariable, machine states, points-to-set variables, etc.. Marking these hot types asfinalgives the JIT more freedom for devirtualization and inlining along the dataflow/fixpoint fast path. No behavioral change; the only externally visible effect is that the affected classes can no longer be subclassed.Effect on JMH benchmarks (JDK 17; 3 forks x 6 one-second iterations per configuration after warm-up; comparisons against
masterusing per-fork means as the unit of analysis, Welch's t-test, one-sided in the improvement direction):DataflowBenchmark.analyzeTestContextInsensitiveSlicerBenchmark.computeBackwardSliceInTestListAtomBenchmark.concatAtoms: 1.5% faster (p = 0.002)AtomBenchmark.startsWith: 0.7% faster (p = 0.017)The context-sensitive dataflow benchmark also improved by 8.7%, though that difference did not reach statistical significance with so few forks (p = 0.088). No other benchmark showed a statistically significant change 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 slicer improvement remains clearly significant (adjusted p ("q") = 0.002) and the small
concatAtomswin is marginal (q = 0.085). The large dataflow improvement has q = 0.187, nominally above the conventional 0.05 cutoff because only 3-4 forks limit our statistical power, but its ~27% effect size and disjoint 99% confidence intervals leave little practical doubt that it is real.