package unit; import java.util.*; /** * OGRETest — Java analogs of three OGRE CWE-407 defects. * * Defect 1 (ogre-0001): Node::~Node — std::find on vector of queued-update nodes. * Slow: ArrayList.remove(Object) — O(N) scan per node destroyed. * Fast: LinkedHashSet.remove(Object) — O(1) per node destroyed. * * Defect 2 (ogre-0002): ResourceGroupManager::_notifyAllResourcesRemoved — std::find * in nested loop erasing resources from a list. * Slow: ArrayList.remove(Object) inside an erase loop — O(N²). * Fast: Build HashSet of to-remove pointers, single-pass removeIf — O(N). * * Defect 3 (ogre-0003): RibbonTrail::clearChain — std::find on parallel index vector * to reverse-map chain-index → node. * Slow: ArrayList.indexOf(chainIndex) — O(N). * Fast: HashMap reverse map — O(1). */ public class OGRETest { static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { // warmup slow.run(); fast.run(); long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000; long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000; double ratio = fOps > 0 ? (double) sOps / fOps : 0; System.out.printf(" %-56s slow:%5dms (%,d ops) fast:%5dms (%,d ops) speedup:%.0fx%n", label, sMs, sOps, fMs, fOps, ratio); } // ----------------------------------------------------------------------- // ogre-0001: Node queued-update removal during bulk destruction // ----------------------------------------------------------------------- static void benchNodeQueuedUpdates(int N) { // SLOW: vector — O(N) scan per node destruction Runnable slow = () -> { List queue = new ArrayList<>(N); Object[] nodes = new Object[N]; for (int i = 0; i < N; i++) { nodes[i] = new Object(); queue.add(nodes[i]); } // simulate N node destructions: each does std::find + erase for (int i = 0; i < N; i++) { queue.remove(nodes[i]); // O(N) scan } }; // FAST: unordered_set — O(1) erase per node destruction Runnable fast = () -> { Set queue = new LinkedHashSet<>(N * 2); Object[] nodes = new Object[N]; for (int i = 0; i < N; i++) { nodes[i] = new Object(); queue.add(nodes[i]); } for (int i = 0; i < N; i++) { queue.remove(nodes[i]); // O(1) hash remove } }; long sOps = (long) N * N / 2; // average scan length N/2 × N destructions long fOps = N; bench(String.format("ogre-0001 Node::~Node queued-update scan N=%d", N), slow, fast, sOps, fOps); } // ----------------------------------------------------------------------- // ogre-0002: ResourceGroupManager notify-removed — nested find+erase // ----------------------------------------------------------------------- static void benchResourceGroupRemove(int R) { // SLOW: std::find on list for each item in arDel — O(R²) Runnable slow = () -> { List resourceList = new ArrayList<>(R); for (int i = 0; i < R; i++) resourceList.add(new Object()); List arDel = new ArrayList<>(resourceList); // remove all for (Object item : arDel) { resourceList.remove(item); // O(R) scan each time } }; // FAST: build HashSet, single-pass removeIf — O(R) Runnable fast = () -> { List resourceList = new ArrayList<>(R); for (int i = 0; i < R; i++) resourceList.add(new Object()); List arDel = new ArrayList<>(resourceList); Set toRemove = new HashSet<>(arDel); resourceList.removeIf(toRemove::contains); // O(R) single pass }; long sOps = (long) R * R; long fOps = R; bench(String.format("ogre-0002 ResourceGroupManager::_notifyRemoved R=%d", R), slow, fast, sOps, fOps); } // ----------------------------------------------------------------------- // ogre-0003: RibbonTrail::clearChain — parallel-vector reverse lookup // ----------------------------------------------------------------------- static void benchRibbonTrailClearChain(int K, int clears) { Object[] nodes = new Object[K]; for (int i = 0; i < K; i++) nodes[i] = new Object(); // SLOW: parallel arrays, indexOf for reverse lookup Runnable slow = () -> { List nodeList = new ArrayList<>(Arrays.asList(nodes)); List chainSegments = new ArrayList<>(); for (int i = 0; i < K; i++) chainSegments.add(i); long dummy = 0; for (int c = 0; c < clears; c++) { int chainIndex = c % K; // std::find on mNodeToChainSegment — O(K) int pos = chainSegments.indexOf(chainIndex); if (pos >= 0) dummy += pos; } if (dummy < 0) System.out.println("never"); }; // FAST: HashMap for reverse lookup — O(1) per clear Runnable fast = () -> { Map chainToNode = new HashMap<>(K * 2); for (int i = 0; i < K; i++) chainToNode.put(i, nodes[i]); long dummy = 0; for (int c = 0; c < clears; c++) { int chainIndex = c % K; // HashMap.get — O(1) Object n = chainToNode.get(chainIndex); if (n != null) dummy++; } if (dummy < 0) System.out.println("never"); }; long sOps = (long) clears * K; long fOps = clears; bench(String.format("ogre-0003 RibbonTrail::clearChain K=%d clears=%d", K, clears), slow, fast, sOps, fOps); } public static void main(String[] args) { System.out.println("OGRE CWE-407 defect benchmarks"); System.out.println("=".repeat(100)); System.out.println("\n[ogre-0001] Node::~Node — msQueuedUpdates linear scan"); benchNodeQueuedUpdates(500); benchNodeQueuedUpdates(2_000); benchNodeQueuedUpdates(10_000); System.out.println("\n[ogre-0002] ResourceGroupManager::_notifyAllResourcesRemoved"); benchResourceGroupRemove(500); benchResourceGroupRemove(2_000); benchResourceGroupRemove(10_000); System.out.println("\n[ogre-0003] RibbonTrail::clearChain — parallel-vector reverse lookup"); benchRibbonTrailClearChain(50, 100_000); benchRibbonTrailClearChain(200, 100_000); benchRibbonTrailClearChain(1_000, 100_000); } }