- netty-0001: DnsResolveContext.finalResult ArrayList.contains O(R²) dedup on DNS records File: resolver-dns/.../dns/DnsResolveContext.java line ~914 Fix: LinkedHashSet gives O(1) dedup with preserved insertion order Ratio: 24.5x at R=50 records - dubbo-0002: MethodWalker.walkHierarchy no visited guard — O(2^D) diamond recursion File: dubbo-rpc-triple/.../rest/util/MethodWalker.java walkHierarchy() Fix: add visited HashSet, return early if already visited Ratio: 8x at D=3 (common Spring proxy depth) - doris-0004: NormalizeRepeat.buildContextWithAlias ImmutableList.contains O(S×G) for GROUPING SETS File: fe-core/.../nereids/rules/analysis/NormalizeRepeat.java buildContextWithAlias() Fix: convert groupingSetExpressions to HashSet before loop — O(1) lookup Ratio: 49x for CUBE(c1..c8), 1000x+ for CUBE(c1..c10)
169 lines
7 KiB
Java
169 lines
7 KiB
Java
/**
|
|
* dubbo-0002: MethodWalker.walkHierarchy diamond recursion O(2^D)
|
|
*
|
|
* Demonstrates the defect: recursive hierarchy traversal without a visited set
|
|
* causes O(2^D) work on diamond interface inheritance patterns.
|
|
*
|
|
* Compile: javac MethodWalkerDiamondAlgorithm.java
|
|
* Run: java MethodWalkerDiamondAlgorithm
|
|
*/
|
|
import java.util.*;
|
|
|
|
public class MethodWalkerDiamondAlgorithm {
|
|
|
|
// Simulated class hierarchy node
|
|
static class ClassNode {
|
|
final String name;
|
|
final ClassNode superClass;
|
|
final List<ClassNode> interfaces;
|
|
final int methodCount;
|
|
|
|
ClassNode(String name, ClassNode superClass, int methodCount, ClassNode... interfaces) {
|
|
this.name = name;
|
|
this.superClass = superClass;
|
|
this.interfaces = Arrays.asList(interfaces);
|
|
this.methodCount = methodCount;
|
|
}
|
|
}
|
|
|
|
// ---- DEFECT: O(2^D) — no visited guard (mirrors MethodWalker.walkHierarchy) ----
|
|
static int[] defectCounter = {0}; // visit counter
|
|
|
|
static Map<String, List<String>> walkDefect(ClassNode clazz) {
|
|
Map<String, List<String>> methodsMap = new HashMap<>();
|
|
walkHierarchyDefect(clazz, methodsMap);
|
|
return methodsMap;
|
|
}
|
|
|
|
static void walkHierarchyDefect(ClassNode clazz, Map<String, List<String>> methodsMap) {
|
|
defectCounter[0]++;
|
|
// Add methods (no dedup for visited classes)
|
|
for (int i = 0; i < clazz.methodCount; i++) {
|
|
methodsMap.computeIfAbsent(clazz.name + ".method" + i, k -> new ArrayList<>())
|
|
.add(clazz.name); // duplicate if revisited
|
|
}
|
|
if (clazz.superClass != null) {
|
|
walkHierarchyDefect(clazz.superClass, methodsMap); // no visited check
|
|
}
|
|
for (ClassNode itf : clazz.interfaces) {
|
|
walkHierarchyDefect(itf, methodsMap); // no visited check — diamond = O(2^D)
|
|
}
|
|
}
|
|
|
|
// ---- FIX: O(N) — visited HashSet guards recursion ----
|
|
static int[] fixCounter = {0};
|
|
|
|
static Map<String, List<String>> walkFixed(ClassNode clazz) {
|
|
Map<String, List<String>> methodsMap = new HashMap<>();
|
|
Set<ClassNode> visited = new HashSet<>();
|
|
walkHierarchyFixed(clazz, methodsMap, visited);
|
|
return methodsMap;
|
|
}
|
|
|
|
static void walkHierarchyFixed(ClassNode clazz, Map<String, List<String>> methodsMap,
|
|
Set<ClassNode> visited) {
|
|
if (!visited.add(clazz)) {
|
|
return; // already visited — skip
|
|
}
|
|
fixCounter[0]++;
|
|
for (int i = 0; i < clazz.methodCount; i++) {
|
|
methodsMap.computeIfAbsent(clazz.name + ".method" + i, k -> new ArrayList<>())
|
|
.add(clazz.name);
|
|
}
|
|
if (clazz.superClass != null) {
|
|
walkHierarchyFixed(clazz.superClass, methodsMap, visited);
|
|
}
|
|
for (ClassNode itf : clazz.interfaces) {
|
|
walkHierarchyFixed(itf, methodsMap, visited);
|
|
}
|
|
}
|
|
|
|
// Build a diamond hierarchy of depth D
|
|
// Base -> I1_L1, I2_L1 -> I1_L2, I2_L2 -> ...
|
|
// at each level, 2 interfaces share all children from the level below
|
|
static ClassNode buildDiamond(int depth) {
|
|
if (depth == 0) {
|
|
return new ClassNode("Base", null, 2);
|
|
}
|
|
ClassNode base = buildDiamond(depth - 1);
|
|
ClassNode left = new ClassNode("L" + depth + "_A", null, 1, base);
|
|
ClassNode right = new ClassNode("L" + depth + "_B", null, 1, base);
|
|
return new ClassNode("Service_D" + depth, null, 3, left, right);
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== dubbo-0002: MethodWalker.walkHierarchy diamond recursion ===");
|
|
System.out.println();
|
|
|
|
// Test basic diamond pattern (depth 1):
|
|
// Base interface -> A, B -> ServiceImpl
|
|
ClassNode base = new ClassNode("Base", null, 2);
|
|
ClassNode interfaceA = new ClassNode("InterfaceA", null, 1, base);
|
|
ClassNode interfaceB = new ClassNode("InterfaceB", null, 1, base);
|
|
ClassNode service = new ClassNode("ServiceImpl", null, 3, interfaceA, interfaceB);
|
|
|
|
System.out.println("--- Basic diamond (depth 1) ---");
|
|
System.out.println("Hierarchy: ServiceImpl -> {InterfaceA, InterfaceB} -> Base");
|
|
|
|
defectCounter[0] = 0;
|
|
fixCounter[0] = 0;
|
|
Map<String, List<String>> defectResult = walkDefect(service);
|
|
Map<String, List<String>> fixResult = walkFixed(service);
|
|
|
|
System.out.printf("Defect visits: %d%n", defectCounter[0]);
|
|
System.out.printf("Fix visits: %d%n", fixCounter[0]);
|
|
|
|
// Verify: defect adds Base methods twice (duplicate entries in list)
|
|
List<String> baseMethods0 = defectResult.get("Base.method0");
|
|
System.out.printf("Base.method0 entries (defect): %d (expect 2, showing duplicate work)%n",
|
|
baseMethods0 != null ? baseMethods0.size() : 0);
|
|
|
|
List<String> baseMethodsFix = fixResult.get("Base.method0");
|
|
System.out.printf("Base.method0 entries (fix): %d (expect 1, no duplicates)%n",
|
|
baseMethodsFix != null ? baseMethodsFix.size() : 0);
|
|
|
|
boolean duplicatesRemoved = (baseMethodsFix == null || baseMethodsFix.size() == 1);
|
|
System.out.printf("No duplicates in fix: %s%n%n", duplicatesRemoved ? "PASS" : "FAIL");
|
|
|
|
// Benchmark scaling: vary diamond depth
|
|
System.out.println("--- Scaling: visits vs diamond depth ---");
|
|
System.out.printf("%-8s %15s %15s %10s%n", "Depth D", "Defect visits", "Fix visits", "Ratio");
|
|
System.out.printf("%-8s %15s %15s %10s%n", "-------", "-------------", "----------", "-----");
|
|
|
|
boolean allPass = true;
|
|
for (int d = 1; d <= 8; d++) {
|
|
ClassNode diamond = buildDiamond(d);
|
|
|
|
defectCounter[0] = 0;
|
|
fixCounter[0] = 0;
|
|
walkDefect(diamond);
|
|
walkFixed(diamond);
|
|
|
|
int dv = defectCounter[0];
|
|
int fv = fixCounter[0];
|
|
double ratio = (double) dv / fv;
|
|
System.out.printf("%-8d %15d %15d %9.1fx%n", d, dv, fv, ratio);
|
|
|
|
// At depth >= 3, ratio should be >= 4x
|
|
if (d >= 3 && ratio < 4.0) {
|
|
System.out.printf(" FAIL: expected ratio >= 4x at depth %d%n", d);
|
|
allPass = false;
|
|
}
|
|
}
|
|
|
|
System.out.println();
|
|
|
|
// Verify fix result is correct (same method set as defect, just deduplicated)
|
|
ClassNode diamond3 = buildDiamond(3);
|
|
Map<String, List<String>> defectResult3 = walkDefect(diamond3);
|
|
Map<String, List<String>> fixResult3 = walkFixed(diamond3);
|
|
boolean sameKeys = defectResult3.keySet().equals(fixResult3.keySet());
|
|
System.out.printf("Same method keys (defect vs fix): %s%n", sameKeys ? "PASS" : "FAIL");
|
|
if (!sameKeys) allPass = false;
|
|
if (!duplicatesRemoved) allPass = false;
|
|
|
|
System.out.println();
|
|
System.out.printf("RESULT: %s%n", allPass ? "PASS" : "FAIL");
|
|
if (!allPass) System.exit(1);
|
|
}
|
|
}
|