203 lines
7.4 KiB
Java
203 lines
7.4 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* artemis-0001: BindingsImpl routeFromCluster O(R×A) → O(R+A)
|
||
*
|
||
* Simulates the routing logic in:
|
||
* artemis-server/.../core/postoffice/impl/BindingsImpl.java
|
||
* Method: routeFromCluster (lines 630–664)
|
||
*
|
||
* Standalone — no JUnit, no Artemis deps.
|
||
*/
|
||
public class BindingsRouteFromClusterAlgorithm {
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Result types
|
||
// -----------------------------------------------------------------------
|
||
|
||
static class RouteResult {
|
||
final long ops;
|
||
final List<Long> routedWithAck;
|
||
final List<Long> routedWithoutAck;
|
||
|
||
RouteResult(long ops, List<Long> routedWithAck, List<Long> routedWithoutAck) {
|
||
this.ops = ops;
|
||
this.routedWithAck = routedWithAck;
|
||
this.routedWithoutAck = routedWithoutAck;
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Slow: ArrayList.contains inside loop (production code)
|
||
// -----------------------------------------------------------------------
|
||
|
||
static RouteResult routeFromClusterSlow(long[] routeIds, long[] ackIds) {
|
||
long ops = 0;
|
||
|
||
// Build idsToAckList as ArrayList (defect)
|
||
List<Long> idsToAckList = new ArrayList<>();
|
||
for (long id : ackIds) {
|
||
idsToAckList.add(id);
|
||
}
|
||
|
||
List<Long> routedWithAck = new ArrayList<>();
|
||
List<Long> routedWithoutAck = new ArrayList<>();
|
||
|
||
for (long bindingID : routeIds) { // O(R) loop
|
||
// List.contains: O(A) scan
|
||
boolean needsAck = false;
|
||
for (Long ackId : idsToAckList) {
|
||
ops++;
|
||
if (ackId.equals(bindingID)) {
|
||
needsAck = true;
|
||
break;
|
||
}
|
||
}
|
||
if (needsAck) {
|
||
routedWithAck.add(bindingID);
|
||
} else {
|
||
routedWithoutAck.add(bindingID);
|
||
}
|
||
}
|
||
|
||
return new RouteResult(ops, routedWithAck, routedWithoutAck);
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Fast: HashSet for O(1) lookup
|
||
// -----------------------------------------------------------------------
|
||
|
||
static RouteResult routeFromClusterFast(long[] routeIds, long[] ackIds) {
|
||
long ops = 0;
|
||
|
||
// Build idsToAckSet as HashSet (fix)
|
||
Set<Long> idsToAckSet = new HashSet<>();
|
||
for (long id : ackIds) {
|
||
ops++;
|
||
idsToAckSet.add(id);
|
||
}
|
||
|
||
List<Long> routedWithAck = new ArrayList<>();
|
||
List<Long> routedWithoutAck = new ArrayList<>();
|
||
|
||
for (long bindingID : routeIds) { // O(R) loop
|
||
ops++;
|
||
if (idsToAckSet.contains(bindingID)) { // O(1)
|
||
routedWithAck.add(bindingID);
|
||
} else {
|
||
routedWithoutAck.add(bindingID);
|
||
}
|
||
}
|
||
|
||
return new RouteResult(ops, routedWithAck, routedWithoutAck);
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Test harness
|
||
// -----------------------------------------------------------------------
|
||
|
||
public static void main(String[] args) {
|
||
int passed = 0;
|
||
int total = 0;
|
||
|
||
// ---- Test 1: basic routing correctness ----
|
||
total++;
|
||
long[] routes = {1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L};
|
||
long[] acks = {2L, 4L, 6L, 8L, 10L};
|
||
|
||
RouteResult slow1 = routeFromClusterSlow(routes, acks);
|
||
RouteResult fast1 = routeFromClusterFast(routes, acks);
|
||
|
||
boolean match1 = new HashSet<>(slow1.routedWithAck).equals(new HashSet<>(fast1.routedWithAck)) &&
|
||
new HashSet<>(slow1.routedWithoutAck).equals(new HashSet<>(fast1.routedWithoutAck));
|
||
if (match1) {
|
||
System.out.println("PASS test1: basic routing — ack=" + slow1.routedWithAck.size() +
|
||
" noack=" + slow1.routedWithoutAck.size());
|
||
passed++;
|
||
} else {
|
||
System.out.println("FAIL test1: routing mismatch");
|
||
}
|
||
|
||
// ---- Test 2: no ACKs required ----
|
||
total++;
|
||
long[] noAcks = {};
|
||
RouteResult slow2 = routeFromClusterSlow(routes, noAcks);
|
||
RouteResult fast2 = routeFromClusterFast(routes, noAcks);
|
||
|
||
boolean match2 = slow2.routedWithAck.isEmpty() && fast2.routedWithAck.isEmpty() &&
|
||
slow2.routedWithoutAck.size() == routes.length &&
|
||
fast2.routedWithoutAck.size() == routes.length;
|
||
if (match2) {
|
||
System.out.println("PASS test2: no-ACK case — all routes without ack");
|
||
passed++;
|
||
} else {
|
||
System.out.println("FAIL test2: no-ACK case mismatch");
|
||
}
|
||
|
||
// ---- Test 3: all ACKs required ----
|
||
total++;
|
||
RouteResult slow3 = routeFromClusterSlow(routes, routes);
|
||
RouteResult fast3 = routeFromClusterFast(routes, routes);
|
||
|
||
boolean match3 = slow3.routedWithAck.size() == routes.length &&
|
||
fast3.routedWithAck.size() == routes.length &&
|
||
slow3.routedWithoutAck.isEmpty() && fast3.routedWithoutAck.isEmpty();
|
||
if (match3) {
|
||
System.out.println("PASS test3: all-ACK case — all routes with ack");
|
||
passed++;
|
||
} else {
|
||
System.out.println("FAIL test3: all-ACK case mismatch");
|
||
}
|
||
|
||
// ---- Test 4: large scale ops ratio ----
|
||
total++;
|
||
int R = 100; // route targets
|
||
int A = 50; // ACK targets
|
||
|
||
long[] bigRoutes = new long[R];
|
||
for (int i = 0; i < R; i++) bigRoutes[i] = i;
|
||
|
||
long[] bigAcks = new long[A];
|
||
for (int i = 0; i < A; i++) bigAcks[i] = i * 2; // every other route needs ACK
|
||
|
||
RouteResult slowBig = routeFromClusterSlow(bigRoutes, bigAcks);
|
||
RouteResult fastBig = routeFromClusterFast(bigRoutes, bigAcks);
|
||
|
||
boolean matchBig = new HashSet<>(slowBig.routedWithAck).equals(new HashSet<>(fastBig.routedWithAck));
|
||
if (!matchBig) {
|
||
System.out.println("FAIL test4: large scale result mismatch");
|
||
} else {
|
||
double ratio = (double) slowBig.ops / fastBig.ops;
|
||
if (ratio >= 10.0) {
|
||
System.out.printf("PASS test4: large scale slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||
slowBig.ops, fastBig.ops, ratio);
|
||
passed++;
|
||
} else {
|
||
System.out.printf("FAIL test4: ratio=%.1fx (need >=10x) slow=%d fast=%d%n",
|
||
ratio, slowBig.ops, fastBig.ops);
|
||
}
|
||
}
|
||
|
||
// ---- Test 5: route IDs not in ACK list ----
|
||
total++;
|
||
long[] disjointAcks = {200L, 201L, 202L}; // none match routes 0..9
|
||
RouteResult slow5 = routeFromClusterSlow(routes, disjointAcks);
|
||
RouteResult fast5 = routeFromClusterFast(routes, disjointAcks);
|
||
|
||
boolean match5 = slow5.routedWithAck.isEmpty() && fast5.routedWithAck.isEmpty();
|
||
if (match5) {
|
||
System.out.println("PASS test5: disjoint ACK IDs — no routes with ack");
|
||
passed++;
|
||
} else {
|
||
System.out.println("FAIL test5: disjoint ACK IDs mismatch");
|
||
}
|
||
|
||
System.out.println("\n" + passed + "/" + total + " PASS");
|
||
|
||
if (passed != total) {
|
||
System.exit(1);
|
||
}
|
||
}
|
||
}
|