java-topology/defects/qemu/unit/QemuSavevmFindSeAlgorithm.java

174 lines
5.7 KiB
Java

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<SaveStateEntry> 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<SaveStateEntry> handlers = new ArrayList<>();
Map<String, SaveStateEntry> 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);
}
}
}