/** * 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 buildContextDefect( List groupingSetExpressions, // ImmutableList in real code List sourceExpressions) { Map 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 buildContextFixed( List groupingSetExpressionsRaw, // same ImmutableList List sourceExpressions) { // One-time O(G) conversion to HashSet fixOps += groupingSetExpressionsRaw.size(); Set groupingSetExpressions = new HashSet<>(groupingSetExpressionsRaw); Map 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 generateCubeGroupingSets(int k) { List 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 generateSourceExpressions(int k, int extra) { List 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 groupingSets, List sources, int iters) { long start = System.nanoTime(); for (int i = 0; i < iters; i++) { Map 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 groupingSetsRaw, List sources, int iters) { long start = System.nanoTime(); for (int i = 0; i < iters; i++) { Set groupingSets = new HashSet<>(groupingSetsRaw); Map 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 smallGroupingSets = Arrays.asList( "c1", "c2", "c3", "c1", "c2", "c1" // flattened ); List sources = Arrays.asList("c1", "c2", "c3", "sum_v", "count_v"); System.out.println("--- Correctness check (ROLLUP(c1,c2,c3)) ---"); defectOps = 0; fixOps = 0; Map defectResult = buildContextDefect(smallGroupingSets, sources); Map 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 gsets = generateCubeGroupingSets(k); List 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 gsets = generateCubeGroupingSets(k); List 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); } }