170 lines
6.8 KiB
Java
170 lines
6.8 KiB
Java
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<Integer,Object> 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<Object> 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<Object> 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<Object> resourceList = new ArrayList<>(R);
|
||
for (int i = 0; i < R; i++) resourceList.add(new Object());
|
||
List<Object> 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<Object> resourceList = new ArrayList<>(R);
|
||
for (int i = 0; i < R; i++) resourceList.add(new Object());
|
||
List<Object> arDel = new ArrayList<>(resourceList);
|
||
|
||
Set<Object> 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<Object> nodeList = new ArrayList<>(Arrays.asList(nodes));
|
||
List<Integer> 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<Integer, Object> 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);
|
||
}
|
||
}
|