java-topology/defects/panda3d/unit/Panda3DTest.java

173 lines
6.8 KiB
Java

package unit;
import java.util.*;
/**
* Panda3D CWE-407 benchmark: std::find on vector vs O(1) unordered set/map.
*
* Defect 1: panda/src/pgraph/camera.cxx line 252
* Camera::remove_display_region — std::find on small_vector<DisplayRegion *>
*
* Defect 2: panda/src/display/graphicsOutput.cxx line 1623
* GraphicsOutput::do_remove_display_region — std::find on pvector<PT(DisplayRegion)>
* Called during window teardown — all N regions removed sequentially → O(N²).
*
* Fix 1: Replace small_vector<DisplayRegion *> with pset<DisplayRegion *>.
* insert/erase/contains all O(1).
* Fix 2: Replace pvector<PT(DisplayRegion)> with pmap<DisplayRegion *, PT(DisplayRegion)>.
* find/erase O(1); iteration for active-region sort still works.
*
* tickets: docs/tickets/panda3d-0001-camera-display-regions-linear-find.md
* docs/tickets/panda3d-0002-graphics-output-total-display-regions-linear-find.md
*/
public class Panda3DTest {
// -----------------------------------------------------------------------
// SLOW: mirrors small_vector + std::find
// -----------------------------------------------------------------------
static class SlowDisplayRegions {
List<Integer> regions = new ArrayList<>(); // Integer = simulated pointer id
void add(int regionId) {
regions.add(regionId);
}
/** Exact mirror of camera.cxx line 251-255 / graphicsOutput.cxx line 1622-1628. */
boolean remove(int regionId) {
int idx = regions.indexOf(regionId); // std::find equivalent
if (idx >= 0) {
regions.remove(idx);
return true;
}
return false;
}
}
// -----------------------------------------------------------------------
// FAST: mirrors pset / pmap with O(1) ops
// -----------------------------------------------------------------------
static class FastDisplayRegions {
Set<Integer> regions = new HashSet<>();
void add(int regionId) {
regions.add(regionId);
}
boolean remove(int regionId) {
return regions.remove(regionId);
}
}
// -----------------------------------------------------------------------
// Bench harness
// -----------------------------------------------------------------------
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
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 r = fOps > 0 ? (double) sOps / fOps : 0;
System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
label, sMs, sOps, fMs, fOps, r);
}
// -----------------------------------------------------------------------
// Scenarios
// -----------------------------------------------------------------------
/**
* Scenario 1 (panda3d-0001): Camera display region removal.
* Add N regions, then remove them all (e.g., camera reassignment loop).
*/
static Runnable slowCameraRemoveAll(int n) {
return () -> {
SlowDisplayRegions dr = new SlowDisplayRegions();
for (int i = 0; i < n; i++) dr.add(i);
for (int i = 0; i < n; i++) dr.remove(i);
};
}
static Runnable fastCameraRemoveAll(int n) {
return () -> {
FastDisplayRegions dr = new FastDisplayRegions();
for (int i = 0; i < n; i++) dr.add(i);
for (int i = 0; i < n; i++) dr.remove(i);
};
}
/**
* Scenario 2 (panda3d-0002): Window teardown — all display regions destroyed.
* Deferred shading pipeline: N = render passes (shadow maps + g-buffer + post).
* Each DisplayRegion destructor calls do_remove_display_region.
*/
static Runnable slowWindowTeardown(int n) {
return () -> {
SlowDisplayRegions total = new SlowDisplayRegions();
for (int i = 0; i < n; i++) total.add(i);
// Teardown: regions destroyed in reverse-creation order (typical destructor order)
for (int i = n - 1; i >= 0; i--) total.remove(i);
};
}
static Runnable fastWindowTeardown(int n) {
return () -> {
FastDisplayRegions total = new FastDisplayRegions();
for (int i = 0; i < n; i++) total.add(i);
for (int i = n - 1; i >= 0; i--) total.remove(i);
};
}
/**
* Scenario 3: VR multi-eye with repeated camera reassignment.
* Each eye reassignment removes from old camera and adds to new — repeated K times
* across N display regions.
*/
static Runnable slowVRReassign(int n, int k) {
return () -> {
SlowDisplayRegions cam = new SlowDisplayRegions();
for (int i = 0; i < n; i++) cam.add(i);
for (int iter = 0; iter < k; iter++) {
for (int i = 0; i < n; i++) {
cam.remove(i);
cam.add(i);
}
}
};
}
static Runnable fastVRReassign(int n, int k) {
return () -> {
FastDisplayRegions cam = new FastDisplayRegions();
for (int i = 0; i < n; i++) cam.add(i);
for (int iter = 0; iter < k; iter++) {
for (int i = 0; i < n; i++) {
cam.remove(i);
cam.add(i);
}
}
};
}
public static void main(String[] args) {
System.out.println("Panda3D CWE-407: std::find on vector vs O(1) hash set/map");
System.out.println(" defect 1: panda/src/pgraph/camera.cxx line 252");
System.out.println(" defect 2: panda/src/display/graphicsOutput.cxx line 1623");
System.out.println();
int N = 800;
long slowOps = (long) N * N / 2;
bench(String.format("camera remove-all N=%d (panda3d-0001)", N),
slowCameraRemoveAll(N), fastCameraRemoveAll(N), slowOps, N);
bench(String.format("window teardown N=%d (panda3d-0002)", N),
slowWindowTeardown(N), fastWindowTeardown(N), slowOps, N);
bench(String.format("VR reassign N=%d x K=10 (panda3d-0001)", N),
slowVRReassign(N, 10), fastVRReassign(N, 10), slowOps * 10, (long) N * 10);
System.out.println();
System.out.println("Fix 1 (camera.cxx): pset<DisplayRegion *> — insert/erase O(1).");
System.out.println("Fix 2 (graphicsOutput.cxx): pmap<DisplayRegion *, PT(...)> — find/erase O(1).");
System.out.println("See patches panda3d-0001-* and panda3d-0002-*");
}
}