Repository navigation
Conversation
During dataflow, AnnotatedTypeFactory.getPath searches from the path of the
method body being analyzed. The search caches only the path to its target, so
each lookup for a node in the body rescans the statements before it. Finding
the defaulting scope (QualifierDefaults.nearestEnclosingExceptLocal) of every
node thus makes checking a method body quadratic in its number of statements.
JFR of the Nullness Checker on a class with 4000 fields and a static method
with 4000 map.put("kN", vN) calls (the shape of checker/jtreg/nullness/
Issue1438.java) had 169 of 590 samples under nearestEnclosingExceptLocal.
After 32 consecutive getPath(TreePath, Tree) misses with the same leaf,
TreePathCacher now records a child-to-parent map of that leaf's subtree and
answers later lookups under it by walking the parent chain. One map is kept
and it is dropped by clear(). The result is the path the scan would find,
including for trees that occur twice (first occurrence in scan order).
Nullness Checker, -proc:only, JDK 21, median of 3 interleaved runs, N calls
and N fields: N=2000 8.08 -> 7.57 s, N=4000 15.14 -> 12.37 s, N=8000
33.99 -> 25.87 s. nearestEnclosingExceptLocal falls to 3 of 448 samples.
javacutil and dataflow sources (209 files), 5 interleaved runs: 21.9 -> 22.2 s,
within noise. Diagnostics are identical.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…PathCacher Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2260.
Problem. During dataflow,
AnnotatedTypeFactory.getPathsearches from the path of the method body being analyzed.TreePathCachercaches only the path to the target, so every lookup for a node in the body rescans the statements before it. Finding the defaulting scope (QualifierDefaults.nearestEnclosingExceptLocal) of every node therefore makes a method body quadratic in its number of statements. JFR on the shape ofchecker/jtreg/nullness/Issue1438.java(4000 fields, one static method with 4000map.put("kN", vN)calls) had 169 of 590 samples undernearestEnclosingExceptLocal.Fix. After 32 consecutive
getPath(TreePath, Tree)misses with the same leaf,TreePathCacherrecords a child-to-parent map of that leaf's subtree and answers later lookups under it by walking the parent chain. One map is kept, replaced when another leaf qualifies, and dropped byclear(). The path returned is the one the scan finds, including for trees that occur twice (the shared type tree ofInteger a, b;): the map keeps the first occurrence in scan order.Measurements. Nullness Checker,
-proc:only, JDK 21, median of 3 interleaved runs, N calls and N fields:nearestEnclosingExceptLocalfalls from 169 of 590 to 3 of 448 samples at N=4000. Real code is unaffected: javacutil and dataflow sources (209 files), 5 interleaved runs, 21.9 s vs 22.2 s, within noise. Diagnostics are identical on every workload.Tried and rejected (recorded in
docs/developer/performance-notes.md): registering the CFG builder's own paths in the cacher (they skip tree levels such asMemberSelectTreeand include artificial trees, so they differ from the real paths), and short-circuitingnearestEnclosingExceptLocalfor declaration trees (it moves the cost toInitializationAnnotatedTypeFactory.isInitializedInTargetStoreand was slower overall).Tests. New
TreePathCacherTest.manyLookupsUnderOneLeafMatchJdkcompares againstTreePath.getPathfor every tree, cold and cached, in forward and reverse order, including trees outside the body and absent from the unit../gradlew :framework:test, the whole./gradlew :checker:test(91 classes) and./gradlew :checker:jtregTestspass.🤖 Generated with Claude Code