package unit; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * qemu-0001: find_se() O(N²) during migration load * * Models QEMU's savevm handler registry and find_se() lookup. * SLOW: linear scan of handler list per lookup (current QEMU code) * FAST: hash map keyed by (idstr, instance_id) (proposed fix) * * During VM load, qemu_loadvm_state_main() calls qemu_loadvm_section_start_full() * for each of the N sections in the migration stream. Each call invokes find_se(), * which does an O(N) linear scan. Total: O(N²). */ public class QemuSavevmFindSeAlgorithm { // --- Data model --- static class SaveStateEntry { String idstr; int instanceId; SaveStateEntry(String idstr, int instanceId) { this.idstr = idstr; this.instanceId = instanceId; } } // --- SLOW: linear scan (current QEMU find_se) --- static class SlowRegistry { List handlers = new ArrayList<>(); long opCount = 0; void register(String idstr, int instanceId) { handlers.add(new SaveStateEntry(idstr, instanceId)); } SaveStateEntry findSe(String idstr, int instanceId) { for (SaveStateEntry se : handlers) { opCount++; if (se.idstr.equals(idstr) && se.instanceId == instanceId) { return se; } } return null; } /** Simulate loading N sections from a migration stream */ int loadSections() { int found = 0; for (SaveStateEntry target : handlers) { SaveStateEntry se = findSe(target.idstr, target.instanceId); if (se != null) found++; } return found; } } // --- FAST: hash map (proposed fix) --- static class FastRegistry { List handlers = new ArrayList<>(); Map handlerMap = new HashMap<>(); long opCount = 0; static String makeKey(String idstr, int instanceId) { return idstr + ":" + instanceId; } void register(String idstr, int instanceId) { SaveStateEntry se = new SaveStateEntry(idstr, instanceId); handlers.add(se); handlerMap.put(makeKey(idstr, instanceId), se); } SaveStateEntry findSe(String idstr, int instanceId) { opCount++; return handlerMap.get(makeKey(idstr, instanceId)); } /** Simulate loading N sections from a migration stream */ int loadSections() { int found = 0; for (SaveStateEntry target : handlers) { SaveStateEntry se = findSe(target.idstr, target.instanceId); if (se != null) found++; } return found; } } // --- Test harness --- static int passed = 0; static int total = 0; static void check(String name, boolean cond) { total++; if (cond) { passed++; System.out.println(" PASS: " + name); } else { System.out.println(" FAIL: " + name); } } public static void main(String[] args) { System.out.println("=== qemu-0001: find_se O(N^2) during migration load ==="); // Build registry of N vmstate handlers (virtio devices, CPUs, PCI, RAM) int N = 500; SlowRegistry slow = new SlowRegistry(); FastRegistry fast = new FastRegistry(); // Register N distinct handlers with various idstrs for (int i = 0; i < N; i++) { String idstr = "device-" + (i % 50); // 50 unique device types int instanceId = i / 50; // up to 10 instances per type slow.register(idstr, instanceId); fast.register(idstr, instanceId); } // Correctness: both find the same entries int slowFound = slow.loadSections(); int fastFound = fast.loadSections(); check("slow finds all " + N + " handlers", slowFound == N); check("fast finds all " + N + " handlers", fastFound == N); check("slow and fast agree", slowFound == fastFound); // Specific lookup SaveStateEntry slowSe = slow.findSe("device-0", 0); SaveStateEntry fastSe = fast.findSe("device-0", 0); check("slow finds device-0:0", slowSe != null); check("fast finds device-0:0", fastSe != null); SaveStateEntry slowMiss = slow.findSe("nonexistent", 99); SaveStateEntry fastMiss = fast.findSe("nonexistent", 99); check("slow returns null for unknown", slowMiss == null); check("fast returns null for unknown", fastMiss == null); // Reset opcount before load simulation slow.opCount = 0; fast.opCount = 0; slow.loadSections(); fast.loadSections(); long slowOps = slow.opCount; long fastOps = fast.opCount; double ratio = (double) slowOps / fastOps; System.out.println(); System.out.println("N=" + N + " handlers, loading N sections from migration stream:"); System.out.printf(" SLOW ops (linear scan): %,d%n", slowOps); System.out.printf(" FAST ops (hash lookup): %,d%n", fastOps); System.out.printf(" Ratio: %.1fx%n", ratio); check("slow is O(N^2): ops >= N*(N/4)", slowOps >= (long) N * N / 4); check("fast is O(N): ops <= N*2", fastOps <= (long) N * 2); check("ratio >= 5x", ratio >= 5.0); System.out.println(); System.out.println(passed + "/" + total + " PASS"); if (passed != total) { System.exit(1); } } }