import java.util.*; /** * Unit test for opensmtpd-0001: mta_handle_envelope() TAILQ_FOREACH O(N²) task lookup. * * In OpenSMTPD mta.c, mta_handle_envelope() searches relay->tasks with a * TAILQ_FOREACH linear scan to find the mta_task matching the incoming * envelope's msgid. For a relay holding N tasks, each of M envelopes * triggers an O(N) scan → O(M×N) total work. * * The fix replaces the scan with a tree_get() call on a per-relay * task_by_msgid splay-tree index → O(M × log N) total work. * * This test models the hotspot with: * - RELAY_COUNT relays * - TASK_COUNT tasks per relay * - ENVELOPE_COUNT envelopes distributed across relays/tasks * * It counts comparison operations for both approaches and asserts that * the hash-map (O(1) amortised, models tree O(log N)) approach is at * least RATIO_THRESHOLD times cheaper than the linear scan. */ public class OpensmtpdTest { static final int RELAY_COUNT = 100; static final int TASK_COUNT = 100; // tasks per relay static final int ENVELOPE_COUNT = 100; // envelopes per relay static final int RATIO_THRESHOLD = 10; // Counters shared across simulations static long linearComparisons = 0; static long hashLookups = 0; // Simulate a relay as a list of task msgids (TAILQ) + a HashMap index static class Relay { final List taskQueue = new ArrayList<>(); final Map taskIndex = new HashMap<>(); final Object TASK = new Object(); void addTask(long msgid) { taskQueue.add(msgid); taskIndex.put(msgid, TASK); } /** * Original code: TAILQ_FOREACH scan — counts each list element * examined as one comparison operation. */ boolean findLinear(long msgid) { for (long id : taskQueue) { linearComparisons++; if (id == msgid) return true; } return false; } /** * Patched code: tree_get (modelled as HashMap.get) — counts one * operation regardless of list length. */ boolean findHash(long msgid) { hashLookups++; return taskIndex.containsKey(msgid); } } public static void main(String[] args) { // Build RELAY_COUNT relays, each pre-loaded with TASK_COUNT tasks. List relays = new ArrayList<>(RELAY_COUNT); Random rng = new Random(0xdeadbeefL); for (int r = 0; r < RELAY_COUNT; r++) { Relay relay = new Relay(); // Use deterministic msgids: relay*1000 + task index for (int t = 0; t < TASK_COUNT; t++) { relay.addTask((long)(r * 1000 + t)); } relays.add(relay); } // --- LINEAR SCAN simulation --- linearComparisons = 0; for (int r = 0; r < RELAY_COUNT; r++) { Relay relay = relays.get(r); for (int e = 0; e < ENVELOPE_COUNT; e++) { // Each envelope targets an existing task (worst-case: last task) long msgid = (long)(r * 1000 + (TASK_COUNT - 1)); boolean found = relay.findLinear(msgid); if (!found) { System.err.println("FAIL: linear search missed existing task"); System.exit(1); } } } long linearTotal = linearComparisons; // --- HASH LOOKUP simulation --- hashLookups = 0; for (int r = 0; r < RELAY_COUNT; r++) { Relay relay = relays.get(r); for (int e = 0; e < ENVELOPE_COUNT; e++) { long msgid = (long)(r * 1000 + (TASK_COUNT - 1)); boolean found = relay.findHash(msgid); if (!found) { System.err.println("FAIL: hash lookup missed existing task"); System.exit(1); } } } long hashTotal = hashLookups; double ratio = (double) linearTotal / (double) hashTotal; System.out.println("opensmtpd-0001: mta_handle_envelope task-lookup benchmark"); System.out.println(" relays : " + RELAY_COUNT); System.out.println(" tasks/relay : " + TASK_COUNT); System.out.println(" envelopes : " + ENVELOPE_COUNT + " per relay"); System.out.println(" linear ops : " + linearTotal + " (TAILQ_FOREACH scan)"); System.out.println(" hash ops : " + hashTotal + " (tree_get / HashMap)"); System.out.printf (" ratio : %.1fx%n", ratio); System.out.println(" threshold : " + RATIO_THRESHOLD + "x"); if (ratio < RATIO_THRESHOLD) { System.err.printf("FAIL: ratio %.1f < threshold %d%n", ratio, RATIO_THRESHOLD); System.exit(1); } System.out.println("ALL PASS"); } }