java-topology/defects/doris/unit/NormalizeRepeatGroupingSetAlgorithm.java
russell@unturf.com 72c98d9af6 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)
2026-03-29 22:11:57 -04:00

192 lines
7.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* doris-0004: NormalizeRepeat.buildContextWithAlias — List.contains O(S×G) for GROUPING SETS
*
* Demonstrates the defect: O(S×G) ImmutableList.contains per source expression
* vs O(G + S) with HashSet for groupingSetExpressions lookup.
*
* Compile: javac NormalizeRepeatGroupingSetAlgorithm.java
* Run: java NormalizeRepeatGroupingSetAlgorithm
*/
import java.util.*;
public class NormalizeRepeatGroupingSetAlgorithm {
// Simulate GROUPING SETS expressions as strings (column references)
// flatExpressions() returns ImmutableList in the real code
// ---- DEFECT: O(S×G) — List.contains per source expression ----
static long defectOps = 0;
static Map<String, String> buildContextDefect(
List<String> groupingSetExpressions, // ImmutableList in real code
List<String> sourceExpressions) {
Map<String, String> result = new LinkedHashMap<>();
for (String expression : sourceExpressions) {
// O(G) scan — the defect
defectOps += groupingSetExpressions.size();
if (groupingSetExpressions.contains(expression)) {
result.put(expression, "GROUPING_SLOT:" + expression);
} else {
result.put(expression, "ALIAS:" + expression);
}
}
return result;
}
// ---- FIX: O(G + S) — HashSet for O(1) membership test ----
static long fixOps = 0;
static Map<String, String> buildContextFixed(
List<String> groupingSetExpressionsRaw, // same ImmutableList
List<String> sourceExpressions) {
// One-time O(G) conversion to HashSet
fixOps += groupingSetExpressionsRaw.size();
Set<String> groupingSetExpressions = new HashSet<>(groupingSetExpressionsRaw);
Map<String, String> result = new LinkedHashMap<>();
for (String expression : sourceExpressions) {
// O(1) lookup
fixOps += 1;
if (groupingSetExpressions.contains(expression)) {
result.put(expression, "GROUPING_SLOT:" + expression);
} else {
result.put(expression, "ALIAS:" + expression);
}
}
return result;
}
// Generate flattened groupingSetExpressions for CUBE(c1..cK)
// CUBE(c1..cK) generates 2^K grouping sets, each being a subset of columns
static List<String> generateCubeGroupingSets(int k) {
List<String> flat = new ArrayList<>();
int numSets = 1 << k; // 2^k
for (int mask = 0; mask < numSets; mask++) {
for (int bit = 0; bit < k; bit++) {
if ((mask & (1 << bit)) != 0) {
flat.add("c" + (bit + 1));
}
}
}
return flat;
}
// Generate source expressions for a query with K grouping columns + extra output cols
static List<String> generateSourceExpressions(int k, int extra) {
List<String> sources = new ArrayList<>();
for (int i = 1; i <= k; i++) sources.add("c" + i); // grouping columns
for (int i = 1; i <= extra; i++) sources.add("agg" + i); // aggregate expressions
return sources;
}
static long benchmarkDefect(List<String> groupingSets, List<String> sources, int iters) {
long start = System.nanoTime();
for (int i = 0; i < iters; i++) {
Map<String, String> result = new LinkedHashMap<>();
for (String expression : sources) {
if (groupingSets.contains(expression)) {
result.put(expression, "GROUPING:" + expression);
} else {
result.put(expression, "ALIAS:" + expression);
}
}
}
return System.nanoTime() - start;
}
static long benchmarkFixed(List<String> groupingSetsRaw, List<String> sources, int iters) {
long start = System.nanoTime();
for (int i = 0; i < iters; i++) {
Set<String> groupingSets = new HashSet<>(groupingSetsRaw);
Map<String, String> result = new LinkedHashMap<>();
for (String expression : sources) {
if (groupingSets.contains(expression)) {
result.put(expression, "GROUPING:" + expression);
} else {
result.put(expression, "ALIAS:" + expression);
}
}
}
return System.nanoTime() - start;
}
public static void main(String[] args) {
System.out.println("=== doris-0004: NormalizeRepeat buildContextWithAlias List.contains O(S×G) ===");
System.out.println();
// Test correctness with simple ROLLUP(c1, c2, c3) = 4 grouping sets
// Grouping sets: {c1,c2,c3}, {c1,c2}, {c1}, {}
List<String> smallGroupingSets = Arrays.asList(
"c1", "c2", "c3", "c1", "c2", "c1" // flattened
);
List<String> sources = Arrays.asList("c1", "c2", "c3", "sum_v", "count_v");
System.out.println("--- Correctness check (ROLLUP(c1,c2,c3)) ---");
defectOps = 0;
fixOps = 0;
Map<String, String> defectResult = buildContextDefect(smallGroupingSets, sources);
Map<String, String> fixResult = buildContextFixed(smallGroupingSets, sources);
System.out.printf("Defect result: %s%n", defectResult);
System.out.printf("Fix result: %s%n", fixResult);
boolean correct = defectResult.equals(fixResult);
System.out.printf("Results match: %s%n%n", correct ? "PASS" : "FAIL");
// Algorithmic operation counts
System.out.println("--- Algorithmic operation counts ---");
System.out.printf("%-8s %10s %12s %10s %8s%n",
"CUBE(K)", "G (flat)", "S (sources)", "Defect S×G", "Fix G+S");
System.out.printf("%-8s %10s %12s %10s %8s%n",
"-------", "--------", "-----------", "-----------", "-------");
boolean allRatiosOk = true;
for (int k : new int[]{3, 5, 7, 8, 10}) {
List<String> gsets = generateCubeGroupingSets(k);
List<String> srcs = generateSourceExpressions(k, k); // K grouping + K aggregates
int G = gsets.size();
int S = srcs.size();
long defectCount = (long) S * G;
long fixCount = G + S;
double ratio = (double) defectCount / fixCount;
System.out.printf("CUBE(%d) %10d %12d %10d %8d [%.1fx]%n",
k, G, S, defectCount, fixCount, ratio);
if (k >= 7 && ratio < 10.0) {
System.out.printf(" FAIL: expected ratio >= 10x for CUBE(%d)%n", k);
allRatiosOk = false;
}
}
System.out.println();
// Performance benchmarks
System.out.println("--- Performance benchmarks ---");
System.out.printf("%-10s %15s %12s %10s%n", "Scenario", "Defect (ns/q)", "Fix (ns/q)", "Speedup");
System.out.printf("%-10s %15s %12s %10s%n", "--------", "-------------", "-----------", "-------");
int iters = 50_000;
for (int k : new int[]{5, 7, 8}) {
List<String> gsets = generateCubeGroupingSets(k);
List<String> srcs = generateSourceExpressions(k, 10);
// Warmup
for (int w = 0; w < 500; w++) {
benchmarkDefect(gsets, srcs, 1);
benchmarkFixed(gsets, srcs, 1);
}
long dt = benchmarkDefect(gsets, srcs, iters);
long ft = benchmarkFixed(gsets, srcs, iters);
double speedup = (double) dt / ft;
System.out.printf("CUBE(%d) %15.0f %12.0f %9.1fx%n",
k,
(double) dt / iters,
(double) ft / iters,
speedup);
}
System.out.println();
System.out.printf("RESULT: %s%n", (correct && allRatiosOk) ? "PASS" : "FAIL");
if (!correct || !allRatiosOk) System.exit(1);
}
}