174 lines
5.7 KiB
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);
|
|
}
|
|
}
|
|
}
|