package unit; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * haproxy-0002: flt_spoe.c duplicate-name detection O(N²) vs O(N log N). * * slow: for each of N message names, walk the linked list of already-added * names (strcmp per entry) — mirrors: * while (*args[cur_arg]) { * list_for_each_entry(ph, &curmphs, list) * if (strcmp(ph->id, args[cur_arg]) == 0) ... * } * * fast: pre-built HashSet; O(1) contains check per name. * * Assert: slowOps > fastOps * (N/2) for N=200 SPOE message names. */ public class HaproxySpoeAlgorithmTest { static long slowOps; static long fastOps; // ---- slow: O(N²) linked-list duplicate detection (defect) --------------- static List slowAddMessages(String[] names) { List registered = new ArrayList<>(); for (String name : names) { boolean dup = false; for (String existing : registered) { // O(R) inner scan slowOps++; if (existing.equals(name)) { dup = true; break; } } if (!dup) { registered.add(name); } } return registered; } // ---- fast: O(N) HashSet duplicate detection (fix) ----------------------- static List fastAddMessages(String[] names) { List registered = new ArrayList<>(); Set seen = new HashSet<>(); for (String name : names) { fastOps++; // O(1) HashSet lookup if (!seen.contains(name)) { seen.add(name); registered.add(name); } } return registered; } // ---- benchmark driver --------------------------------------------------- public static void main(String[] args) { final int N = 200; // SPOE message names in a single directive // Generate N unique names (worst case: no duplicates = maximum list walks) String[] names = new String[N]; for (int i = 0; i < N; i++) { names[i] = "spoe-msg-" + i; } slowOps = 0; fastOps = 0; List slowResult = slowAddMessages(names); List fastResult = fastAddMessages(names); // Verify correctness: same number of unique entries if (slowResult.size() != fastResult.size()) { System.err.printf("FAIL: slow=%d entries fast=%d entries (mismatch)%n", slowResult.size(), fastResult.size()); System.exit(1); } long ratio = slowOps / Math.max(fastOps, 1); // Expect at least N/2 - 1 ratio since average list length = N/2 // (worst-case unique names: average walk = N/2, last entry walks N-1 steps) boolean pass = slowOps > fastOps * (N / 2 - 2); System.out.printf("haproxy-0002 slow=%d fast=%d ratio=%dx %s%n", slowOps, fastOps, ratio, pass ? "PASS" : "FAIL"); if (!pass) { System.err.printf("FAIL: expected slowOps(%d) > fastOps(%d) * %d%n", slowOps, fastOps, N / 2 - 2); System.exit(1); } } }