190 lines
7.2 KiB
Java
190 lines
7.2 KiB
Java
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<String> for event_intercepts / pipes (List.contains = O(n))
|
|
* - Fast: HashSet<String> 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<String> of K intercepted events.
|
|
// Per broadcast: check event membership for all subscribers.
|
|
// -----------------------------------------------------------------------
|
|
|
|
static long dispatchSlow(List<String[]> 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<Set<String>> subscriberSets, String event, int broadcastCount) {
|
|
long matches = 0;
|
|
for (int b = 0; b < broadcastCount; b++) {
|
|
for (Set<String> 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<String> 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<String> 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<String> of K event names
|
|
List<String[]> slowSubscribers = new ArrayList<>(N_SUBSCRIBERS);
|
|
List<Set<String>> 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<intercepts> vs HashSet<intercepts>",
|
|
() -> 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<String[]> s = new ArrayList<>(5_000);
|
|
List<Set<String>> 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.");
|
|
}
|
|
}
|