// Unit test for fbneo-0001: BurnDrvGetIndex O(N) linear scan -> O(1) hash lookup. // Models the defect: with 5000+ game drivers, every call to BurnDrvGetIndex // (game load, state load, Kaillera/netplay) scanned the full driver array. // Fix: build std::unordered_map at BurnLibInit time, O(1) lookup. #include #include #include #include #include #include #include // Minimal driver struct matching FBNeo's BurnDriver struct BurnDriver { const char* szShortName; }; // Original: O(N) linear scan static int drv_get_index_original(const std::vector& pDriver, const char* szName) { if (!szName) return -1; for (int i = 0; i < (int)pDriver.size(); i++) { if (strcmp(szName, pDriver[i]->szShortName) == 0) return i; } return -1; } // Patched: O(1) hash lookup struct PatchedIndex { std::unordered_map map; void build(const std::vector& pDriver) { map.clear(); map.reserve(pDriver.size()); for (int i = 0; i < (int)pDriver.size(); i++) map[pDriver[i]->szShortName] = i; } int get(const char* szName) const { if (!szName) return -1; auto it = map.find(szName); return (it != map.end()) ? it->second : -1; } }; int main() { // Build driver pool modeling FBNeo's ~5000 game drivers const int N_DRIVERS = 5000; std::vector names; names.reserve(N_DRIVERS); for (int i = 0; i < N_DRIVERS; i++) names.push_back("drv_" + std::to_string(i)); std::vector drivers(N_DRIVERS); std::vector pDriver(N_DRIVERS); for (int i = 0; i < N_DRIVERS; i++) { drivers[i].szShortName = names[i].c_str(); pDriver[i] = &drivers[i]; } PatchedIndex idx; idx.build(pDriver); // Test 1: found at index 0 (best case original, same patched) { int orig = drv_get_index_original(pDriver, "drv_0"); int patched = idx.get("drv_0"); assert(orig == 0); assert(patched == 0); printf("PASS: lookup first driver\n"); } // Test 2: found at last index (worst case original) { int orig = drv_get_index_original(pDriver, "drv_4999"); int patched = idx.get("drv_4999"); assert(orig == 4999); assert(patched == 4999); printf("PASS: lookup last driver\n"); } // Test 3: not found returns -1 { int orig = drv_get_index_original(pDriver, "no_such_game"); int patched = idx.get("no_such_game"); assert(orig == -1); assert(patched == -1); printf("PASS: missing driver returns -1\n"); } // Test 4: null returns -1 { int orig = drv_get_index_original(pDriver, nullptr); int patched = idx.get(nullptr); assert(orig == -1); assert(patched == -1); printf("PASS: null name returns -1\n"); } // Test 5: correctness across all drivers { for (int i = 0; i < N_DRIVERS; i += 100) { assert(drv_get_index_original(pDriver, names[i].c_str()) == i); assert(idx.get(names[i].c_str()) == i); } printf("PASS: correctness across all %d drivers\n", N_DRIVERS); } // Test 6: performance -- lookup all N drivers once (covers entire range, no elision) { volatile int sink = 0; auto t0 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < N_DRIVERS; i++) sink += drv_get_index_original(pDriver, names[i].c_str()); auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < N_DRIVERS; i++) sink += idx.get(names[i].c_str()); auto t2 = std::chrono::high_resolution_clock::now(); (void)sink; double orig_us = std::chrono::duration_cast(t1 - t0).count(); double patch_us = std::chrono::duration_cast(t2 - t1).count(); double ratio = orig_us / (patch_us > 0.0 ? patch_us : 1.0); printf("PASS: performance N=%d all-driver scan: original=%.0fus patched=%.0fus ratio=%.1fx\n", N_DRIVERS, orig_us, patch_us, ratio); // Scanning all N=5000 drivers takes O(N^2/2) comparisons vs O(N) hash lookups. // Even with vectorization the ratio must exceed 5x at N=5000. assert(ratio > 5.0); } printf("ALL TESTS PASSED\n"); return 0; }