package unit; import java.util.*; /** * PhoenixTest — CWE-407 benchmark for Phoenix channel event_intercepts (phoenix-0001) * and router scope pipes (phoenix-0002). * * Models the Elixir data structures in Java: * - Slow: List for event_intercepts / pipes (List.contains = O(n)) * - Fast: HashSet for event_intercepts / pipes (HashSet.contains = O(1)) * * phoenix-0001: dispatch() iterates all subscribers checking event in event_intercepts. * Slow: O(subscribers * intercepts) list scan per broadcast. * Fast: O(subscribers) with HashSet.contains(event). * * phoenix-0002: pipe_through() checks for duplicate pipes. * Slow: O(new_pipes * existing_pipes) nested list scans. * Fast: O(new_pipes) with HashSet.contains. */ public class PhoenixTest { static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { // warmup slow.run(); fast.run(); long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000; long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000; double r = fMs > 0 ? (double) sMs / fMs : 0; System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.1fx%n", label, sMs, sOps, fMs, fOps, r); } // ----------------------------------------------------------------------- // phoenix-0001: channel dispatch — event in event_intercepts // Models: N subscribers each holding a List of K intercepted events. // Per broadcast: check event membership for all subscribers. // ----------------------------------------------------------------------- static long dispatchSlow(List subscribers, String event, int broadcastCount) { long matches = 0; for (int b = 0; b < broadcastCount; b++) { for (String[] intercepts : subscribers) { // Elixir: event in event_intercepts (List.member? — O(k)) for (String e : intercepts) { if (e.equals(event)) { matches++; break; } } } } return matches; } static long dispatchFast(List> subscriberSets, String event, int broadcastCount) { long matches = 0; for (int b = 0; b < broadcastCount; b++) { for (Set intercepts : subscriberSets) { // MapSet.member? — O(1) if (intercepts.contains(event)) { matches++; } } } return matches; } // ----------------------------------------------------------------------- // phoenix-0002: pipe_through duplicate check // Models: accumulating P pipes one-by-one, checking for duplicates each time. // ----------------------------------------------------------------------- static int pipeAccumulateSlow(int totalPipes) { // Elixir: pipes is a List — duplicate check via Enum.find(&1 in pipes) List pipes = new ArrayList<>(); int duplicatesFound = 0; for (int i = 0; i < totalPipes; i++) { String newPipe = "pipeline_" + i; // O(n) scan for duplicate boolean isDuplicate = false; for (String p : pipes) { if (p.equals(newPipe)) { isDuplicate = true; break; } } if (!isDuplicate) { // O(n) append via list concat simulation pipes.add(newPipe); } else { duplicatesFound++; } } return duplicatesFound; } static int pipeAccumulateFast(int totalPipes) { // Elixir fix: pipes is a MapSet — duplicate check via MapSet.member? Set pipes = new HashSet<>(); int duplicatesFound = 0; for (int i = 0; i < totalPipes; i++) { String newPipe = "pipeline_" + i; // O(1) membership check if (pipes.contains(newPipe)) { duplicatesFound++; } else { pipes.add(newPipe); // O(1) add } } return duplicatesFound; } public static void main(String[] args) { System.out.println("Phoenix CWE-407 Benchmarks"); System.out.println("=========================="); System.out.println(); // --- phoenix-0001: channel dispatch --- System.out.println("phoenix-0001: channel dispatch event_intercepts (N=10000 subscribers, K=10 intercepts, 200 broadcasts)"); int N_SUBSCRIBERS = 10_000; int K_INTERCEPTS = 10; int BROADCASTS = 200; // Build slow: each subscriber holds a List of K event names List slowSubscribers = new ArrayList<>(N_SUBSCRIBERS); List> fastSubscribers = new ArrayList<>(N_SUBSCRIBERS); String[] interceptNames = new String[K_INTERCEPTS]; for (int k = 0; k < K_INTERCEPTS; k++) { interceptNames[k] = "event_" + k; } for (int i = 0; i < N_SUBSCRIBERS; i++) { slowSubscribers.add(interceptNames.clone()); fastSubscribers.add(new HashSet<>(Arrays.asList(interceptNames))); } // Target event is the last one (worst case for list scan) String targetEvent = "event_" + (K_INTERCEPTS - 1); long sOps = (long) N_SUBSCRIBERS * BROADCASTS; long fOps = sOps; bench( "dispatch: event in List vs HashSet", () -> dispatchSlow(slowSubscribers, targetEvent, BROADCASTS), () -> dispatchFast(fastSubscribers, targetEvent, BROADCASTS), sOps, fOps ); // Vary K — show the O(K) scaling System.out.println(); System.out.println("phoenix-0001: vary K (intercepts per subscriber), N=5000, 100 broadcasts"); int[] kValues = {1, 5, 10, 20, 50}; for (int K : kValues) { List s = new ArrayList<>(5_000); List> f = new ArrayList<>(5_000); String[] kNames = new String[K]; for (int k = 0; k < K; k++) kNames[k] = "ev_" + k; for (int i = 0; i < 5_000; i++) { s.add(kNames.clone()); f.add(new HashSet<>(Arrays.asList(kNames))); } String ev = "ev_" + (K - 1); long ops = 5_000L * 100; bench( String.format("K=%2d intercepts: List.contains vs HashSet.contains", K), () -> dispatchSlow(s, ev, 100), () -> dispatchFast(f, ev, 100), ops, ops ); } // --- phoenix-0002: pipe accumulation --- System.out.println(); System.out.println("phoenix-0002: router scope pipe accumulation (P=2000 pipelines)"); int P = 2_000; bench( "pipe_through: ArrayList dup-check vs HashSet dup-check", () -> pipeAccumulateSlow(P), () -> pipeAccumulateFast(P), P, P ); System.out.println(); System.out.println("Done."); } }