import java.util.*; /** * Unit test for PCSX2 pcsx2-0001: Patch::ReloadEnabledLists and EnablePatches * use std::find on std::vector for membership checks inside loops, giving O(N^2). * * Four sites in ReloadEnabledLists: * 1. disabled_patches lookup O(E*D) * 2. prev_enabled_cheats lookup O(C*P) * 3. prev_enabled_patches lookup O(P*P) * 4. EnablePatches enable_list lookup O(G*E) * * Fix: convert lookup vectors to HashSet for O(1) membership. * * Defect file: pcsx2/Patch.cpp lines 616, 630, 637, 651 */ public class PatchReloadEnabledListsTest { // --- Defective: linear scan on list --- static List reloadEnabledListsDefective( List enabledPatches, List disabledPatches, List prevEnabledPatches) { // Filter disabled patches: O(E*D) with list.contains List filtered = new ArrayList<>(); for (String patch : enabledPatches) { if (!disabledPatches.contains(patch)) { // O(D) scan per patch filtered.add(patch); } } // Find newly enabled: O(F*P) with list.contains List justEnabled = new ArrayList<>(); for (String p : filtered) { if (!prevEnabledPatches.contains(p)) { // O(P) scan per patch justEnabled.add(p); } } return justEnabled; } // --- Fixed: hash set for O(1) lookups --- static List reloadEnabledListsFixed( List enabledPatches, List disabledPatches, List prevEnabledPatches) { Set disabledSet = new HashSet<>(disabledPatches); Set prevSet = new HashSet<>(prevEnabledPatches); List filtered = new ArrayList<>(); for (String patch : enabledPatches) { if (!disabledSet.contains(patch)) { // O(1) filtered.add(patch); } } List justEnabled = new ArrayList<>(); for (String p : filtered) { if (!prevSet.contains(p)) { // O(1) justEnabled.add(p); } } return justEnabled; } // --- EnablePatches defective: O(G*E) --- static int enablePatchesDefective(List patchGroups, List enableList) { int count = 0; for (String name : patchGroups) { if (!name.isEmpty() && !enableList.contains(name)) // O(E) per group continue; count++; } return count; } // --- EnablePatches fixed: O(G+E) --- static int enablePatchesFixed(List patchGroups, List enableList) { Set enableSet = new HashSet<>(enableList); int count = 0; for (String name : patchGroups) { if (!name.isEmpty() && !enableSet.contains(name)) // O(1) continue; count++; } return count; } public static void main(String[] args) { int N = 500; // Build test data List enabled = new ArrayList<>(); List disabled = new ArrayList<>(); List prevEnabled = new ArrayList<>(); List patchGroups = new ArrayList<>(); List enableList = new ArrayList<>(); for (int i = 0; i < N; i++) { String name = "patch_" + i; enabled.add(name); patchGroups.add(name); enableList.add(name); if (i % 5 == 0) disabled.add(name); if (i < N / 2) prevEnabled.add(name); } // Correctness check List resultDef = reloadEnabledListsDefective(enabled, disabled, prevEnabled); List resultFix = reloadEnabledListsFixed(enabled, disabled, prevEnabled); assert resultDef.equals(resultFix) : "ReloadEnabledLists mismatch"; int countDef = enablePatchesDefective(patchGroups, enableList); int countFix = enablePatchesFixed(patchGroups, enableList); assert countDef == countFix : "EnablePatches mismatch"; // Warm up for (int i = 0; i < 200; i++) { reloadEnabledListsDefective(enabled, disabled, prevEnabled); reloadEnabledListsFixed(enabled, disabled, prevEnabled); } // Benchmark ReloadEnabledLists int ITER = 2000; long t0 = System.nanoTime(); for (int i = 0; i < ITER; i++) { reloadEnabledListsDefective(enabled, disabled, prevEnabled); } long defectNs = System.nanoTime() - t0; t0 = System.nanoTime(); for (int i = 0; i < ITER; i++) { reloadEnabledListsFixed(enabled, disabled, prevEnabled); } long fixedNs = System.nanoTime() - t0; double ratio = (double) defectNs / fixedNs; System.out.printf("ReloadEnabledLists N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n", N, defectNs / 1e6, fixedNs / 1e6, ratio); // Benchmark EnablePatches t0 = System.nanoTime(); for (int i = 0; i < ITER; i++) { enablePatchesDefective(patchGroups, enableList); } long epDefNs = System.nanoTime() - t0; t0 = System.nanoTime(); for (int i = 0; i < ITER; i++) { enablePatchesFixed(patchGroups, enableList); } long epFixNs = System.nanoTime() - t0; double epRatio = (double) epDefNs / epFixNs; System.out.printf("EnablePatches N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n", N, epDefNs / 1e6, epFixNs / 1e6, epRatio); assert ratio > 2.0 : "Expected >2x speedup for ReloadEnabledLists, got " + ratio; assert epRatio > 2.0 : "Expected >2x speedup for EnablePatches, got " + epRatio; System.out.println("PASS"); } }