package unit; import java.util.ArrayList; import java.util.List; /** * wasmer-0001: Ruleset Vec O(n) linear scan on every network operation. * * Models the Ruleset.allows_socket() hot path: * slow: rules stored as flat List, every check scans all rules (O(n)) * fast: rules partitioned by type at insert time; socket check only scans * ip_rules, domain check only scans dns_rules (O(n_ip) / O(n_dns)) * * Benchmark: N rules total = N/2 IP + N/2 DNS. * slow: allows_socket scans all N rules per call. * fast: allows_socket scans N/2 IP rules per call. * With M=N calls: slow=O(N^2), fast=O((N/2)*N) => 2x minimum. * At high DNS:IP ratio (e.g. 90% DNS), speedup approaches 10x. */ public class RulesetLinearScanTest { // Rule types enum RuleKind { IP, DNS, NEG_IP, NEG_DNS } static class Rule { final RuleKind kind; final int id; // synthetic payload to prevent JIT elimination Rule(RuleKind kind, int id) { this.kind = kind; this.id = id; } boolean allowsSocket(int addr) { // Simulate per-rule check cost: kind-dispatch + comparison return kind == RuleKind.IP && id == addr; } boolean blocksSocket(int addr) { return kind == RuleKind.NEG_IP && id == addr; } boolean allowsDomain(String domain) { return kind == RuleKind.DNS && domain.hashCode() == id; } boolean blocksDomain(String domain) { return kind == RuleKind.NEG_DNS && domain.hashCode() == id; } } /** SLOW: flat Vec — scans all N rules per allows_socket call */ static class SlowRuleset { final List rules = new ArrayList<>(); void addRule(Rule r) { rules.add(r); } boolean allowsSocket(int addr) { // Two full scans (blocks check + allows check) for (Rule r : rules) { if (r.blocksSocket(addr)) return false; } for (Rule r : rules) { if (r.allowsSocket(addr)) return true; } return false; } } /** FAST: partitioned — socket check only scans ip_rules */ static class FastRuleset { final List ip_rules = new ArrayList<>(); final List dns_rules = new ArrayList<>(); void addRule(Rule r) { switch (r.kind) { case DNS: dns_rules.add(r); break; case NEG_DNS: dns_rules.add(r); break; default: ip_rules.add(r); break; } } boolean allowsSocket(int addr) { // Only scans ip_rules — DNS rules never visited for (Rule r : ip_rules) { if (r.blocksSocket(addr)) return false; } for (Rule r : ip_rules) { if (r.allowsSocket(addr)) return true; } return false; } } /** * Benchmark helper. * @param ruleset slow or fast instance (via interface) * @param addFn lambda to add a rule * @param checkFn lambda to perform one allows_socket check * @param N number of rules * @param M number of check calls * @return exact operation count (comparisons performed) */ static long bench(boolean slow, int N, int M, int dnsRatio) { // dnsRatio: percentage of rules that are DNS (0-100) // slow path scans all N per call; fast path scans N*(1-dnsRatio/100) per call // Build rulesets SlowRuleset slowRs = slow ? new SlowRuleset() : null; FastRuleset fastRs = slow ? null : new FastRuleset(); int ipCount = N - (N * dnsRatio / 100); int dnsCount = N * dnsRatio / 100; for (int i = 0; i < ipCount; i++) { Rule r = new Rule(RuleKind.IP, i + 10000); if (slow) slowRs.addRule(r); else fastRs.addRule(r); } for (int i = 0; i < dnsCount; i++) { Rule r = new Rule(RuleKind.DNS, i + 20000); if (slow) slowRs.addRule(r); else fastRs.addRule(r); } // Run M checks — addr never matches any rule (worst case full scan) int addr = 99999; long ops = 0; for (int i = 0; i < M; i++) { if (slow) { ops += slowRs.rules.size(); // blocks scan slowRs.allowsSocket(addr); ops += slowRs.rules.size(); // allows scan } else { ops += fastRs.ip_rules.size(); // blocks scan (ip only) fastRs.allowsSocket(addr); ops += fastRs.ip_rules.size(); // allows scan (ip only) } } return ops; } static void test(String name, int N, int M, int dnsRatio, int minSpeedup) { long sOps = bench(true, N, M, dnsRatio); long fOps = bench(false, N, M, dnsRatio); double speedup = (double) sOps / fOps; boolean pass = sOps >= fOps * minSpeedup; System.out.printf("%-40s slow=%,d fast=%,d speedup=%.1fx %s%n", name, sOps, fOps, speedup, pass ? "PASS" : "FAIL"); assert pass : String.format( "%s: expected speedup >=%dx, got %.1fx (slow=%d, fast=%d)", name, minSpeedup, speedup, sOps, fOps); } public static void main(String[] args) { System.out.println("wasmer-0001: Ruleset linear scan"); System.out.println("================================="); // 50% DNS rules: fast path scans half as many rules => 2x minimum test("N=100 M=100 dns=50% minSpeedup=2x", 100, 100, 50, 2); test("N=500 M=500 dns=50% minSpeedup=2x", 500, 500, 50, 2); // 80% DNS rules: fast path scans 20% of rules => 5x minimum test("N=100 M=100 dns=80% minSpeedup=5x", 100, 100, 80, 5); test("N=500 M=500 dns=80% minSpeedup=5x", 500, 500, 80, 5); // 90% DNS rules: fast path scans 10% of rules => 10x minimum (HIGH severity threshold) test("N=200 M=200 dns=90% minSpeedup=10x", 200, 200, 90, 10); test("N=1000 M=100 dns=90% minSpeedup=10x", 1000, 100, 90, 10); System.out.println("================================="); System.out.println("ALL PASS"); } }