package unit; import java.util.*; import java.util.stream.*; /** * Standalone unit test for trino-0001: * PushDownDereferenceThroughJoin — List.contains() in stream filter → O(N²). * * Simulates the two hot loops in PushDownDereferenceThroughJoin.apply(): * Loop 1: forEach over dereferenceAssignments, calling outputSymbols.contains() * Loop 2: stream.filter(s -> outputSymbols.contains(s)) * * Compile: javac -d . PushDownDerefJoinAlgorithm.java * Run: java unit.PushDownDerefJoinAlgorithm */ public class PushDownDerefJoinAlgorithm { // ── Node ───────────────────────────────────────────────────────────────── static class Symbol { final String name; Symbol(String name) { this.name = name; } @Override public boolean equals(Object o) { return o instanceof Symbol && name.equals(((Symbol) o).name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return name; } } // ── Defective: uses List.contains() O(N) per look-up ──────────────────── static class DefectivePushDown { List filterSymbols( List referred, List nodeOutputSymbols) { // List → O(N) .contains // Mirrors: referredSymbols.stream().filter(s -> nodeOutput.contains(s)) return referred.stream() .filter(s -> nodeOutputSymbols.contains(s)) // CWE-407 site .collect(Collectors.toList()); } Map classifyDerefs( List derefs, List leftOutput, // List → O(N) .contains List rightOutput) { // List → O(N) .contains Map result = new LinkedHashMap<>(); for (Symbol d : derefs) { if (leftOutput.contains(d)) { // CWE-407 site result.put(d, new Symbol("left:" + d.name)); } else if (rightOutput.contains(d)) { // CWE-407 site result.put(d, new Symbol("right:" + d.name)); } } return result; } } // ── Fixed: converts to Set once, then O(1) per look-up ────────────────── static class FixedPushDown { List filterSymbols( List referred, List nodeOutputSymbols) { Set outputSet = new HashSet<>(nodeOutputSymbols); // O(N) once return referred.stream() .filter(outputSet::contains) // O(1) each .collect(Collectors.toList()); } Map classifyDerefs( List derefs, List leftOutput, List rightOutput) { Set leftSet = new HashSet<>(leftOutput); // O(N) once Set rightSet = new HashSet<>(rightOutput); // O(N) once Map result = new LinkedHashMap<>(); for (Symbol d : derefs) { if (leftSet.contains(d)) { // O(1) result.put(d, new Symbol("left:" + d.name)); } else if (rightSet.contains(d)) { // O(1) result.put(d, new Symbol("right:" + d.name)); } } return result; } } // ── Result container ───────────────────────────────────────────────────── static class Result { final String label; final List filteredLeft; final List filteredRight; final Map classified; final long ns; Result(String label, List fl, List fr, Map cl, long ns) { this.label = label; this.filteredLeft = fl; this.filteredRight = fr; this.classified = cl; this.ns = ns; } } // ── Test driver ────────────────────────────────────────────────────────── static List symbols(int n, String prefix) { List out = new ArrayList<>(n); for (int i = 0; i < n; i++) out.add(new Symbol(prefix + i)); return out; } static Result runDefective(int N, int D) { List leftOutput = symbols(N, "lout"); List rightOutput = symbols(N, "rout"); // derefs: first half from left, second from right List derefs = new ArrayList<>(); for (int i = 0; i < D / 2; i++) derefs.add(leftOutput.get(i % N)); for (int i = 0; i < D / 2; i++) derefs.add(rightOutput.get(i % N)); List referred = new ArrayList<>(); referred.addAll(leftOutput.subList(0, Math.min(N / 2, N))); referred.addAll(rightOutput.subList(0, Math.min(N / 2, N))); DefectivePushDown alg = new DefectivePushDown(); long t0 = System.nanoTime(); Map classified = alg.classifyDerefs(derefs, leftOutput, rightOutput); List fl = alg.filterSymbols(referred, leftOutput); List fr = alg.filterSymbols(referred, rightOutput); long ns = System.nanoTime() - t0; return new Result("DEFECTIVE", fl, fr, classified, ns); } static Result runFixed(int N, int D) { List leftOutput = symbols(N, "lout"); List rightOutput = symbols(N, "rout"); List derefs = new ArrayList<>(); for (int i = 0; i < D / 2; i++) derefs.add(leftOutput.get(i % N)); for (int i = 0; i < D / 2; i++) derefs.add(rightOutput.get(i % N)); List referred = new ArrayList<>(); referred.addAll(leftOutput.subList(0, Math.min(N / 2, N))); referred.addAll(rightOutput.subList(0, Math.min(N / 2, N))); FixedPushDown alg = new FixedPushDown(); long t0 = System.nanoTime(); Map classified = alg.classifyDerefs(derefs, leftOutput, rightOutput); List fl = alg.filterSymbols(referred, leftOutput); List fr = alg.filterSymbols(referred, rightOutput); long ns = System.nanoTime() - t0; return new Result("FIXED", fl, fr, classified, ns); } // ── Assertions ─────────────────────────────────────────────────────────── static void assertEquals(Object expected, Object actual, String msg) { if (!expected.equals(actual)) { throw new AssertionError(msg + ": expected=" + expected + " actual=" + actual); } } static void assertTrue(boolean cond, String msg) { if (!cond) throw new AssertionError(msg); } public static void main(String[] args) { System.out.println("=== trino-0001: PushDownDerefJoinAlgorithm ==="); // Small correctness test { int N = 20, D = 10; Result d = runDefective(N, D); Result f = runFixed(N, D); // Both should classify the same derefs assertEquals(d.classified.size(), f.classified.size(), "classified size"); assertEquals(d.filteredLeft.size(), f.filteredLeft.size(), "filteredLeft size"); assertEquals(d.filteredRight.size(), f.filteredRight.size(), "filteredRight size"); System.out.println("PASS correctness (N=20, D=10)"); } // Benchmark at N=500, D=200 { int N = 500, D = 200; // warm up for (int i = 0; i < 5; i++) { runDefective(N, D); runFixed(N, D); } long defNs = 0, fixNs = 0; int reps = 20; for (int i = 0; i < reps; i++) { defNs += runDefective(N, D).ns; fixNs += runFixed(N, D).ns; } defNs /= reps; fixNs /= reps; double ratio = (double) defNs / fixNs; System.out.printf("BENCH N=500 D=200 reps=%d%n", reps); System.out.printf(" defective avg: %,d ns%n", defNs); System.out.printf(" fixed avg: %,d ns%n", fixNs); System.out.printf(" speedup: %.1fx%n", ratio); assertTrue(ratio >= 2.0, "Expected fixed to be at least 2x faster, got " + ratio + "x"); System.out.println("PASS speedup >= 2x"); } System.out.println("=== ALL PASS ==="); } }