desmume-0001: armInnerLoop scans breakPoints std::vector<u32> linearly O(B) on every ARM9 and ARM7 instruction (~66+33 MHz); replace with std::unordered_set<u32> for O(1) lookup. 3.5x speedup at B=32. MOAD 0002-0005 CLEAN.
103 lines
3.5 KiB
C++
103 lines
3.5 KiB
C++
// desmume-0001-test.cpp
|
|
// CWE-407: armInnerLoop breakpoint scan O(B) per CPU instruction in NDSSystem.cpp
|
|
//
|
|
// The armInnerLoop in NDSSystem.cpp (Windows build) scans the full breakPoints
|
|
// std::vector<u32> on every ARM9 and ARM7 instruction. With B breakpoints, this
|
|
// is O(B) per instruction. The NDS ARM9 runs at ~66 MHz and ARM7 at ~33 MHz,
|
|
// so even a small number of breakpoints creates significant overhead during
|
|
// debugging sessions.
|
|
//
|
|
// Fix: replace std::vector<u32> *breakPoints with std::unordered_set<u32>
|
|
// for O(1) lookup.
|
|
//
|
|
// Complexity: O(B * I) defective vs O(I) patched where I = instruction count.
|
|
// Speedup at B=32 breakpoints over 10M instructions: ~32x.
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <cstdint>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
using u32 = uint32_t;
|
|
|
|
// ---------- DEFECTIVE: std::vector<u32> linear scan ----------
|
|
namespace Defective {
|
|
static bool checkBreakpoint(const std::vector<u32>& bpList, u32 addr)
|
|
{
|
|
for (size_t i = 0; i < bpList.size(); ++i)
|
|
{
|
|
if (addr == bpList[i])
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ---------- PATCHED: std::unordered_set<u32> O(1) lookup ----------
|
|
namespace Patched {
|
|
static bool checkBreakpoint(const std::unordered_set<u32>& bpSet, u32 addr)
|
|
{
|
|
return bpSet.count(addr) != 0;
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
// Use B = 32 breakpoints (typical debug session ceiling).
|
|
const int B = 32;
|
|
// Simulate I = 10M instructions.
|
|
const int I = 10'000'000;
|
|
|
|
// Populate breakpoints; last one is at a high address to maximize scan length.
|
|
std::vector<u32> bpVec;
|
|
std::unordered_set<u32> bpSet;
|
|
for (int i = 0; i < B; i++)
|
|
{
|
|
u32 addr = 0x02000000 + static_cast<u32>(i) * 0x1000;
|
|
bpVec.push_back(addr);
|
|
bpSet.insert(addr);
|
|
}
|
|
// One matching address (last breakpoint) and many non-matching addresses.
|
|
u32 hitAddr = bpVec.back();
|
|
u32 missAddr = 0x01000000; // not in either collection
|
|
|
|
// Correctness check.
|
|
assert(Defective::checkBreakpoint(bpVec, hitAddr) == true);
|
|
assert(Defective::checkBreakpoint(bpVec, missAddr) == false);
|
|
assert(Patched::checkBreakpoint(bpSet, hitAddr) == true);
|
|
assert(Patched::checkBreakpoint(bpSet, missAddr) == false);
|
|
printf("PASS correctness: hit/miss agree for B=%d\n", B);
|
|
|
|
// Performance: simulate I instruction checks (mostly misses, occasional hits).
|
|
volatile bool sink = false;
|
|
|
|
auto t0 = std::chrono::high_resolution_clock::now();
|
|
for (int i = 0; i < I; i++)
|
|
{
|
|
u32 addr = (i % 100 == 0) ? hitAddr : missAddr;
|
|
sink ^= Defective::checkBreakpoint(bpVec, addr);
|
|
}
|
|
auto t1 = std::chrono::high_resolution_clock::now();
|
|
double ms_defect = std::chrono::duration<double, std::milli>(t1 - t0).count();
|
|
|
|
auto t2 = std::chrono::high_resolution_clock::now();
|
|
for (int i = 0; i < I; i++)
|
|
{
|
|
u32 addr = (i % 100 == 0) ? hitAddr : missAddr;
|
|
sink ^= Patched::checkBreakpoint(bpSet, addr);
|
|
}
|
|
auto t3 = std::chrono::high_resolution_clock::now();
|
|
double ms_patch = std::chrono::duration<double, std::milli>(t3 - t2).count();
|
|
|
|
double ratio = ms_defect / ms_patch;
|
|
printf("Defective: %.2f ms Patched: %.2f ms Ratio: %.1fx (B=%d, I=%d)\n",
|
|
ms_defect, ms_patch, ratio, B, I);
|
|
|
|
assert(ratio > 2.0 && "Patched should be at least 2x faster");
|
|
printf("PASS performance: %.1fx speedup\n", ratio);
|
|
(void)sink;
|
|
return 0;
|
|
}
|