netty-0001 + dubbo-0002 + doris-0004: DNS dedup O(R²); MethodWalker O(2^D); NormalizeRepeat O(S×G); count 621→624
- 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)
This commit is contained in:
parent
4c52d9ee51
commit
72c98d9af6
6 changed files with 898 additions and 0 deletions
169
defects/dubbo/unit/MethodWalkerDiamondAlgorithm.java
Normal file
169
defects/dubbo/unit/MethodWalkerDiamondAlgorithm.java
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue