165 lines
5.2 KiB
Java
165 lines
5.2 KiB
Java
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<String> 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<QueueKey> 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<BindingEntry> slowExport(List<BindingEntry> bindings, List<QueueKey> qnames) {
|
||
List<BindingEntry> 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<String> buildQNamesSet(List<QueueKey> qnames) {
|
||
Set<String> 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<String> qnamesSet) {
|
||
if (!b.isQueue) return true;
|
||
fastOps++;
|
||
return qnamesSet.contains(b.dest + "\0" + b.vhost);
|
||
}
|
||
|
||
static List<BindingEntry> fastExport(List<BindingEntry> bindings, List<QueueKey> qnames) {
|
||
Set<String> qnamesSet = buildQNamesSet(qnames);
|
||
List<BindingEntry> 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<QueueKey> qnames = new ArrayList<>(Q);
|
||
for (int i = 0; i < Q; i++) {
|
||
qnames.add(new QueueKey("queue-" + i, "vhost-" + (i % 5)));
|
||
}
|
||
|
||
List<BindingEntry> 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<BindingEntry> slowResult = slowExport(bindings, qnames);
|
||
|
||
// FAST run
|
||
fastOps = 0;
|
||
List<BindingEntry> 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);
|
||
}
|
||
}
|
||
}
|