90 lines
2.9 KiB
Java
90 lines
2.9 KiB
Java
package unit;
|
||
|
||
import java.util.HashSet;
|
||
import java.util.Set;
|
||
import java.util.concurrent.CopyOnWriteArrayList;
|
||
|
||
/**
|
||
* pulsar-0005 — PartialRoundRobinMessageRouterImpl CopyOnWriteArrayList.contains O(N×L) → HashSet O(N)
|
||
*
|
||
* The hot path in getOrCreatePartialList() is the filter lambda:
|
||
* .filter(e -> !partialList.contains(e))
|
||
*
|
||
* This benchmark isolates the membership test — N calls to contains()
|
||
* over a CopyOnWriteArrayList of size L vs a HashSet snapshot.
|
||
*
|
||
* SLOW: CopyOnWriteArrayList.contains(e) → O(L) per call → O(N × L) total
|
||
* FAST: HashSet.contains(e) → O(1) per call → O(N) total
|
||
*/
|
||
public class PulsarPartialRoundRobinRouterTest {
|
||
|
||
static long benchSlow(int N, int L) {
|
||
CopyOnWriteArrayList<Integer> partialList = new CopyOnWriteArrayList<>();
|
||
for (int i = 0; i < L; i++) {
|
||
partialList.add(i * 2); // even indices present
|
||
}
|
||
long start = System.nanoTime();
|
||
int found = 0;
|
||
for (int i = 0; i < N; i++) {
|
||
if (!partialList.contains(i)) found++; // O(L) per call
|
||
}
|
||
return System.nanoTime() - start;
|
||
}
|
||
|
||
static long benchFast(int N, int L) {
|
||
CopyOnWriteArrayList<Integer> partialList = new CopyOnWriteArrayList<>();
|
||
for (int i = 0; i < L; i++) {
|
||
partialList.add(i * 2);
|
||
}
|
||
// FIX: snapshot to HashSet before the filter loop
|
||
Set<Integer> existing = new HashSet<>(partialList);
|
||
long start = System.nanoTime();
|
||
int found = 0;
|
||
for (int i = 0; i < N; i++) {
|
||
if (!existing.contains(i)) found++; // O(1) per call
|
||
}
|
||
return System.nanoTime() - start;
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
int passed = 0;
|
||
int failed = 0;
|
||
int minRatio = 5;
|
||
|
||
// [N = numPartitions, L = partialList.size() at expand time]
|
||
int[][] cases = {
|
||
{500, 250},
|
||
{2000, 1000},
|
||
{5000, 2500},
|
||
};
|
||
|
||
// warmup
|
||
for (int w = 0; w < 3; w++) {
|
||
benchSlow(200, 100);
|
||
benchFast(200, 100);
|
||
}
|
||
|
||
for (int[] c : cases) {
|
||
int N = c[0];
|
||
int L = c[1];
|
||
|
||
long slowTotal = 0, fastTotal = 0;
|
||
int reps = 10;
|
||
for (int r = 0; r < reps; r++) {
|
||
slowTotal += benchSlow(N, L);
|
||
fastTotal += benchFast(N, L);
|
||
}
|
||
long slowAvg = slowTotal / reps;
|
||
long fastAvg = fastTotal / reps;
|
||
|
||
double ratio = fastAvg > 0 ? (double) slowAvg / fastAvg : 999.0;
|
||
boolean ok = ratio >= minRatio;
|
||
System.out.printf(" N=%-5d L=%-5d slow=%8dns fast=%7dns ratio=%.1fx %s%n",
|
||
N, L, slowAvg, fastAvg, ratio, ok ? "PASS" : "FAIL");
|
||
if (ok) passed++; else failed++;
|
||
}
|
||
|
||
System.out.printf("%nTotal: %d/%d PASS%n", passed, passed + failed);
|
||
if (failed > 0) System.exit(1);
|
||
}
|
||
}
|