B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
2.4 KiB
0005 — InferenceContext.isEquiv(): O(B²) bound-list equality via List.containsAll()
Status: open
Severity: performance — low
Component: jdk.compiler / com.sun.tools.javac.comp.InferenceContext$ReachabilityVisitor
Root Cause
InferenceContext.java:506 — isEquiv():
boolean isEquiv(UndetVar from, Type t, InferenceBound boundKind) {
UndetVar uv = (UndetVar)asUndetVar(t);
for (InferenceBound ib : InferenceBound.values()) {
List<Type> b1 = from.getBounds(ib);
...
List<Type> b2 = uv.getBounds(ib);
...
if (!b1.containsAll(b2) || !b2.containsAll(b1)) { // O(B²)
return false;
}
}
return true;
}
b1.containsAll(b2) on com.sun.tools.javac.util.List (javac's own linked-list) does a linear contains() check per element of b2 — O(|b1| × |b2|). Called twice.
This is a set-equality check expressed as two mutual containment checks on linear lists. Should use Set or sort-and-compare.
Call Context
isEquiv() is called from ReachabilityVisitor.visitUndetVar() which is called during inference context minimization (InferenceContext.min()). Minimization fires when the compiler attempts to reduce the inference context to its minimal reachable set before solving.
Bounds lists are typically small (1–10 elements), so O(B²) is not severe in practice. Impact is lower than 0001–0003 but follows the same pattern.
Fix
// Replace containsAll on linked lists with Set comparison:
if (!new LinkedHashSet<>(b1).equals(new LinkedHashSet<>(b2))) {
return false;
}
Or, if getBounds() returned a Set instead of a List, the comparison would be direct.
Summary
| # | Location | Defect | Complexity | Module |
|---|---|---|---|---|
| 0001 | GraphUtils$Tarjan | stack.contains(n) | O(V²) Tarjan | jdk.compiler |
| 0002 | InferenceGraph.findNode() + closure() | O(N) scan + uncached DFS | O(N³) total | jdk.compiler |
| 0003 | ModuleHashesBuilder$TopoSorter | Deque.contains() | O(V²) topo sort | java.base |
| 0004 | Dependencies$Node.addDependency() | List.contains() dedup | O(N) per add | jdk.compiler |
| 0005 | InferenceContext.isEquiv() | List.containsAll() | O(B²) per call | jdk.compiler |
All five share the root pattern: linear collection membership check where O(1) is achievable.