package unit; import java.util.ArrayList; import java.util.List; /** * wasmer-0002: WasiThread signal Vec O(n) dedup on every signal delivery. * * Models the thread.signal() hot path: * slow: signals stored as Vec, 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 with linear contains for dedup */ static class SlowSignalSet { final List 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"); } }