package unit; import java.util.*; /** * SynapseTest — CWE-407 benchmark for synapse-0001 and synapse-0002 * * synapse-0001: server_notices/resource_limits_server_notices.py * `if event_id in referenced_events` + `referenced_events.remove()` inside for-loop * Slow: List.contains + List.remove (both O(n)) called n times → O(n²) * Fast: Set.contains + Set.remove → O(n) * * synapse-0002: handlers/sync.py * `if user_id in user_ids_in_room` where user_ids_in_room is a Sequence[str] (List) * Called inside loop over rooms; each room may have U members * Slow: List.contains → O(U) per room, O(R*U) total * Fast: Set.contains → O(1) per room, O(R+U) total * * compile: javac -d . SynapseTest.java && java -ea unit.SynapseTest */ public class SynapseTest { static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { slow.run(); fast.run(); // warmup 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 ratio = fOps > 0 ? (double) sOps / fOps : 0; System.out.printf(" %-60s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n", label, sMs, sOps, fMs, fOps, ratio); } // ----------------------------------------------------------------------- // synapse-0001: referenced_events List.remove in loop vs Set.discard // ----------------------------------------------------------------------- /** Slow: List.contains + List.remove in loop — O(n²) */ static long slowServerNotices(int n) { List referencedEvents = new ArrayList<>(); for (int i = 0; i < n; i++) referencedEvents.add("event-" + i); // Simulate: for event_id, event in events.items(): if event_id in referenced_events: remove long ops = 0; // half the events are ServerNoticeMsgType matches List toRemove = new ArrayList<>(); for (int i = 0; i < n; i += 2) toRemove.add("event-" + i); for (String eventId : toRemove) { ops += referencedEvents.size(); // cost of .contains() scan if (referencedEvents.contains(eventId)) { ops += referencedEvents.size(); // cost of .remove() scan + shift referencedEvents.remove(eventId); } } return ops; } /** Fast: Set.discard — O(n) */ static long fastServerNotices(int n) { Set referencedSet = new LinkedHashSet<>(); for (int i = 0; i < n; i++) referencedSet.add("event-" + i); long ops = 0; List toRemove = new ArrayList<>(); for (int i = 0; i < n; i += 2) toRemove.add("event-" + i); for (String eventId : toRemove) { ops += 1; // O(1) set remove referencedSet.remove(eventId); } return ops; } // ----------------------------------------------------------------------- // synapse-0002: user_ids_in_room List linear scan vs Set // ----------------------------------------------------------------------- /** Slow: List.contains for each room membership check — O(R * U) */ static long slowSyncUserInRoom(int numRooms, int usersPerRoom) { long ops = 0; String targetUser = "user-42"; for (int r = 0; r < numRooms; r++) { // get_users_in_room returns a List List userList = new ArrayList<>(); for (int u = 0; u < usersPerRoom; u++) userList.add("user-" + u); // if user_id in user_ids_in_room: O(U) scan ops += userList.size(); // cost of linear scan userList.contains(targetUser); } return ops; } /** Fast: frozenset lookup — O(R + U) */ static long fastSyncUserInRoom(int numRooms, int usersPerRoom) { long ops = 0; String targetUser = "user-42"; for (int r = 0; r < numRooms; r++) { Set userSet = new HashSet<>(); for (int u = 0; u < usersPerRoom; u++) userSet.add("user-" + u); ops += 1; // O(1) set lookup userSet.contains(targetUser); } return ops; } public static void main(String[] args) { System.out.println("SynapseTest — CWE-407 benchmarks"); System.out.println(); int passed = 0; int total = 0; // --- synapse-0001 --- int N_EVENTS = 2000; long[] slowOps0001 = {0}; long[] fastOps0001 = {0}; Runnable slow0001 = () -> slowOps0001[0] = slowServerNotices(N_EVENTS); Runnable fast0001 = () -> fastOps0001[0] = fastServerNotices(N_EVENTS); // Pre-run to populate ops counts for display slowOps0001[0] = slowServerNotices(N_EVENTS); fastOps0001[0] = fastServerNotices(N_EVENTS); bench("synapse-0001 server_notices List.remove vs Set.discard (n=" + N_EVENTS + ")", slow0001, fast0001, slowOps0001[0], fastOps0001[0]); total++; if (slowOps0001[0] > fastOps0001[0] * 10L) { System.out.println(" synapse-0001 PASS (slow ops=" + slowOps0001[0] + " > 10x fast ops=" + fastOps0001[0] + ")"); passed++; } else { System.out.println(" synapse-0001 FAIL (slow=" + slowOps0001[0] + " fast=" + fastOps0001[0] + ")"); } assert slowOps0001[0] > fastOps0001[0] * 10L : "synapse-0001: slow ops not 10x fast ops"; // --- synapse-0002 --- int NUM_ROOMS = 50; int USERS_PER_ROOM = 5000; long[] slowOps0002 = {0}; long[] fastOps0002 = {0}; Runnable slow0002 = () -> slowOps0002[0] = slowSyncUserInRoom(NUM_ROOMS, USERS_PER_ROOM); Runnable fast0002 = () -> fastOps0002[0] = fastSyncUserInRoom(NUM_ROOMS, USERS_PER_ROOM); slowOps0002[0] = slowSyncUserInRoom(NUM_ROOMS, USERS_PER_ROOM); fastOps0002[0] = fastSyncUserInRoom(NUM_ROOMS, USERS_PER_ROOM); bench("synapse-0002 sync user_in_room List vs Set (rooms=" + NUM_ROOMS + " users=" + USERS_PER_ROOM + ")", slow0002, fast0002, slowOps0002[0], fastOps0002[0]); total++; if (slowOps0002[0] > fastOps0002[0] * 100L) { System.out.println(" synapse-0002 PASS (slow ops=" + slowOps0002[0] + " > 100x fast ops=" + fastOps0002[0] + ")"); passed++; } else { System.out.println(" synapse-0002 FAIL (slow=" + slowOps0002[0] + " fast=" + fastOps0002[0] + ")"); } assert slowOps0002[0] > fastOps0002[0] * 100L : "synapse-0002: slow ops not 100x fast ops"; System.out.println(); System.out.println(passed + "/" + total + " PASS"); } }