java-topology/defects/wasmer/unit/SignalVecDedupTest.java

149 lines
5.6 KiB
Java

package unit;
import java.util.ArrayList;
import java.util.List;
/**
* wasmer-0002: WasiThread signal Vec<Signal> O(n) dedup on every signal delivery.
*
* Models the thread.signal() hot path:
* slow: signals stored as Vec<Signal>, contains() used for dedup => O(n) per insert
* fast: signals stored as long bitmask => O(1) insert with automatic dedup
*
* Also models has_signal(query: &[Signal]):
* slow: O(S * Q) nested loop (S=pending signals, Q=query len)
* fast: O(Q) build query mask + O(1) bitmask AND
*
* The signal domain is bounded to POSIX signals 1..31 (~30 distinct values).
* Benchmark: S signals pending, Q queries, N deliver calls.
* slow: O(S) per deliver (contains check) + O(S*Q) per has_signal call
* fast: O(1) per deliver + O(Q) per has_signal call
*/
public class SignalVecDedupTest {
static final int MAX_SIGNAL = 31; // POSIX signals 1..31
/** SLOW: Vec<Signal> with linear contains for dedup */
static class SlowSignalSet {
final List<Integer> signals = new ArrayList<>();
/** Returns ops performed */
long signal(int sig) {
long ops = 0;
for (int s : signals) { ops++; if (s == sig) return ops; } // contains check
signals.add(sig);
return ops + 1; // add op
}
/** Returns ops performed */
long hasSignal(int[] query) {
long ops = 0;
for (int s : signals) {
for (int q : query) {
ops++;
if (s == q) return ops;
}
}
return ops;
}
}
/** FAST: long bitmask — O(1) insert and O(Q) query */
static class FastSignalSet {
long mask = 0L;
/** Returns ops performed (always 1: single bit-set) */
long signal(int sig) {
mask |= (1L << sig);
return 1;
}
/** Returns ops performed: Q bit-set ops + 1 AND */
long hasSignal(int[] query) {
long qmask = 0L;
for (int q : query) qmask |= (1L << q);
return query.length + 1; // Q ops to build mask + 1 AND
}
}
static long benchSignal(boolean slow, int N, int S) {
// Deliver N signals cycling through S distinct signal values
SlowSignalSet slowSet = slow ? new SlowSignalSet() : null;
FastSignalSet fastSet = slow ? null : new FastSignalSet();
long totalOps = 0;
for (int i = 0; i < N; i++) {
int sig = (i % S) + 1; // signals 1..S
if (slow) totalOps += slowSet.signal(sig);
else totalOps += fastSet.signal(sig);
}
return totalOps;
}
static long benchHasSignal(boolean slow, int S, int Q, int calls) {
// S signals pending, query Q signals, repeat 'calls' times
SlowSignalSet slowSet = slow ? new SlowSignalSet() : null;
FastSignalSet fastSet = slow ? null : new FastSignalSet();
// Pre-fill with S pending signals (signals 1..S)
for (int s = 1; s <= S; s++) {
if (slow) slowSet.signal(s); else fastSet.signal(s);
}
// Query for signals that are NOT pending (worst-case: full scan required)
// Use signals S+1..S+Q (not in the pending set)
int[] queryArr = new int[Q];
for (int q = 0; q < Q; q++) queryArr[q] = S + q + 1;
long totalOps = 0;
for (int c = 0; c < calls; c++) {
if (slow) totalOps += slowSet.hasSignal(queryArr);
else totalOps += fastSet.hasSignal(queryArr);
}
return totalOps;
}
static void testSignal(String name, int N, int S, int minSpeedup) {
long sOps = benchSignal(true, N, S);
long fOps = benchSignal(false, N, S);
double speedup = (double) sOps / fOps;
boolean pass = sOps >= fOps * minSpeedup;
System.out.printf(" signal %-42s slow=%,d fast=%,d speedup=%.1fx %s%n",
name, sOps, fOps, speedup, pass ? "PASS" : "FAIL");
assert pass : String.format(
"signal %s: expected speedup >=%dx, got %.1fx", name, minSpeedup, speedup);
}
static void testHasSignal(String name, int S, int Q, int calls, int minSpeedup) {
long sOps = benchHasSignal(true, S, Q, calls);
long fOps = benchHasSignal(false, S, Q, calls);
double speedup = (double) sOps / fOps;
boolean pass = sOps >= fOps * minSpeedup;
System.out.printf(" has_sig %-42s slow=%,d fast=%,d speedup=%.1fx %s%n",
name, sOps, fOps, speedup, pass ? "PASS" : "FAIL");
assert pass : String.format(
"has_signal %s: expected speedup >=%dx, got %.1fx", name, minSpeedup, speedup);
}
public static void main(String[] args) {
System.out.println("wasmer-0002: Signal Vec dedup");
System.out.println("=============================");
System.out.println("-- signal() delivery --");
// S=30 signals: after all 30 are in the set, each new deliver scans ~30 items
// vs O(1) bitmask. With N=1000 delivers cycling through 30: slow costs ~15000 ops.
testSignal("N=100 S=30 minSpeedup=10x", 100, 30, 10);
testSignal("N=1000 S=30 minSpeedup=10x", 1000, 30, 10);
System.out.println("-- has_signal() query --");
// S=30 pending, Q=10 query: slow=S*Q=300 per call, fast=Q+1=11 per call => ~27x
testHasSignal("S=30 Q=10 calls=100 minSpeedup=15x", 30, 10, 100, 15);
testHasSignal("S=30 Q=30 calls=100 minSpeedup=15x", 30, 30, 100, 15);
System.out.println("=============================");
System.out.println("ALL PASS");
}
}