package unit; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * rmq-0005: rabbit_mgmt_wm_definitions export_binding O(B×Q) → O(B+Q) * * Simulates the binding export scan in rabbit_mgmt_wm_definitions.erl. * SLOW: export_binding uses List.contains({name,vhost}) for each of B bindings. * FAST: pre-build a HashSet of queue keys, then O(1) lookup per binding. */ public class ExportBindingAlgorithm { // Simulated binding entry: destination name and vhost static final class BindingEntry { final String dest; final String vhost; final boolean isQueue; // dest_type == queue BindingEntry(String dest, String vhost, boolean isQueue) { this.dest = dest; this.vhost = vhost; this.isQueue = isQueue; } } // Simulated queue-name tuple: {name, vhost} static final class QueueKey { final String name; final String vhost; QueueKey(String name, String vhost) { this.name = name; this.vhost = vhost; } @Override public boolean equals(Object o) { if (!(o instanceof QueueKey)) return false; QueueKey q = (QueueKey) o; return name.equals(q.name) && vhost.equals(q.vhost); } @Override public int hashCode() { return 31 * name.hashCode() + vhost.hashCode(); } } // SLOW: O(B×Q) — lists:member({Dest,VHost}, QNames) inside list comprehension static long slowOps = 0; static boolean exportBindingSlow(BindingEntry b, List qnames) { if (!b.isQueue) return true; // exchange binding, always export for (QueueKey q : qnames) { slowOps++; if (q.name.equals(b.dest) && q.vhost.equals(b.vhost)) { return true; } } return false; } static List slowExport(List bindings, List qnames) { List result = new ArrayList<>(); for (BindingEntry b : bindings) { if (exportBindingSlow(b, qnames)) { result.add(b); } } return result; } // FAST: O(B+Q) — pre-build HashSet, then O(1) per binding static long fastOps = 0; static Set buildQNamesSet(List qnames) { Set s = new HashSet<>(qnames.size() * 2); for (QueueKey q : qnames) { fastOps++; // one insertion per queue s.add(q.name + "\0" + q.vhost); } return s; } static boolean exportBindingFast(BindingEntry b, Set qnamesSet) { if (!b.isQueue) return true; fastOps++; return qnamesSet.contains(b.dest + "\0" + b.vhost); } static List fastExport(List bindings, List qnames) { Set qnamesSet = buildQNamesSet(qnames); List result = new ArrayList<>(); for (BindingEntry b : bindings) { if (exportBindingFast(b, qnamesSet)) { result.add(b); } } return result; } public static void main(String[] args) { // Build test data: Q queues, B bindings (all queue-type, all matching) int Q = 1000; // queue count int B = 5000; // binding count List qnames = new ArrayList<>(Q); for (int i = 0; i < Q; i++) { qnames.add(new QueueKey("queue-" + i, "vhost-" + (i % 5))); } List bindings = new ArrayList<>(B); for (int i = 0; i < B; i++) { // Each binding points to a queue that exists — worst-case scan to find it int idx = (i * 7 + 3) % Q; // spread across queues bindings.add(new BindingEntry( qnames.get(idx).name, qnames.get(idx).vhost, true )); } // SLOW run slowOps = 0; List slowResult = slowExport(bindings, qnames); // FAST run fastOps = 0; List fastResult = fastExport(bindings, qnames); // Correctness check boolean pass = true; if (slowResult.size() != fastResult.size()) { System.out.println("FAIL: result size mismatch: slow=" + slowResult.size() + " fast=" + fastResult.size()); pass = false; } // Verify same bindings exported (order may differ, but all B bindings match) if (slowResult.size() != B) { System.out.println("FAIL: expected all " + B + " bindings exported, got slow=" + slowResult.size()); pass = false; } // Ratio check double ratio = (double) slowOps / (double) fastOps; System.out.printf("N=%d/%d B=%d Q=%d slow=%d ops fast=%d ops ratio=%.1fx%n", B, Q, B, Q, slowOps, fastOps, ratio); if (ratio < 5.0) { System.out.println("FAIL: ratio too low (expected >= 5x)"); pass = false; } if (pass) { System.out.println("1/1 PASS"); } else { System.exit(1); } } }