java-topology/defects/phoenix/unit/PhoenixTest.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.");
}
}