whitepaper: 398/185 — wave6d (rails-0012..16, jsc-0001/2, vtk, sm-0002, redis/valkey-0003, helm-0002/3, k8s-0003)
This commit is contained in:
parent
eb9612e4bf
commit
3735145aa5
47 changed files with 3488 additions and 33 deletions
175
defects/vtk/unit/SurfaceNetsLabelCollectAlgorithm.java
Normal file
175
defects/vtk/unit/SurfaceNetsLabelCollectAlgorithm.java
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Models vtkGeneralizedSurfaceNets3D::RequestData() auto-label collection.
|
||||
*
|
||||
* When no explicit segmentation labels are provided, the filter collects
|
||||
* unique region IDs by iterating all numPts scalars:
|
||||
*
|
||||
* SLOW: std::find() on a growing std::vector<double> — O(numPts × numLabels)
|
||||
* FAST: std::unordered_set<double> insertion test — O(numPts)
|
||||
*
|
||||
* CWE-407: VTK Filters/Meshing/vtkGeneralizedSurfaceNets3D.cxx:1150
|
||||
*/
|
||||
public class SurfaceNetsLabelCollectAlgorithm {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Slow (defective) implementation.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class SlowCollect {
|
||||
long totalOps = 0;
|
||||
|
||||
/** Collect unique non-negative region IDs in order of first appearance. */
|
||||
List<Double> collect(double[] regions) {
|
||||
List<Double> autoLabels = new ArrayList<>();
|
||||
for (double regionId : regions) {
|
||||
if (regionId >= 0) {
|
||||
boolean found = false;
|
||||
for (double existing : autoLabels) { // O(k) scan — the defect
|
||||
totalOps++;
|
||||
if (existing == regionId) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
autoLabels.add(regionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return autoLabels;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Fast (fixed) implementation.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class FastCollect {
|
||||
long totalOps = 0;
|
||||
|
||||
List<Double> collect(double[] regions) {
|
||||
HashSet<Double> seen = new HashSet<>();
|
||||
List<Double> autoLabels = new ArrayList<>();
|
||||
for (double regionId : regions) {
|
||||
totalOps++; // one O(1) hash op per point
|
||||
if (regionId >= 0 && seen.add(regionId)) {
|
||||
autoLabels.add(regionId);
|
||||
}
|
||||
}
|
||||
Collections.sort(autoLabels); // deterministic ordering
|
||||
return autoLabels;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static double[] buildRegions(int numPts, int numLabels) {
|
||||
double[] regions = new double[numPts];
|
||||
for (int i = 0; i < numPts; i++) {
|
||||
regions[i] = i % numLabels; // round-robin label assignment
|
||||
}
|
||||
return regions;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static int passed = 0;
|
||||
static int total = 0;
|
||||
|
||||
static void check(String label, boolean condition) {
|
||||
total++;
|
||||
if (condition) {
|
||||
passed++;
|
||||
System.out.println(" PASS " + label);
|
||||
} else {
|
||||
System.out.println(" FAIL " + label);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== SurfaceNetsLabelCollectAlgorithm ===");
|
||||
|
||||
// --- Correctness: small known input ---
|
||||
{
|
||||
double[] regions = {0, 1, 2, 1, 0, 3, -1, 2, 3};
|
||||
SlowCollect slow = new SlowCollect();
|
||||
FastCollect fast = new FastCollect();
|
||||
List<Double> slowResult = slow.collect(regions);
|
||||
List<Double> fastResult = fast.collect(regions);
|
||||
|
||||
check("small: slow finds 4 labels", slowResult.size() == 4);
|
||||
check("small: fast finds 4 labels", fastResult.size() == 4);
|
||||
// Both should contain {0,1,2,3}; fast is sorted
|
||||
Collections.sort(slowResult);
|
||||
check("small: results equal after sort", slowResult.equals(fastResult));
|
||||
}
|
||||
|
||||
// --- Correctness: single label ---
|
||||
{
|
||||
double[] regions = {5, 5, 5, 5};
|
||||
SlowCollect slow = new SlowCollect();
|
||||
FastCollect fast = new FastCollect();
|
||||
List<Double> sr = slow.collect(regions);
|
||||
List<Double> fr = fast.collect(regions);
|
||||
check("single-label: slow size==1", sr.size() == 1);
|
||||
check("single-label: fast size==1", fr.size() == 1);
|
||||
check("single-label: value==5.0", fr.get(0) == 5.0);
|
||||
}
|
||||
|
||||
// --- Correctness: all negative (no output labels) ---
|
||||
{
|
||||
double[] regions = {-1, -2, -3};
|
||||
SlowCollect slow = new SlowCollect();
|
||||
FastCollect fast = new FastCollect();
|
||||
check("all-neg: slow empty", slow.collect(regions).isEmpty());
|
||||
check("all-neg: fast empty", fast.collect(regions).isEmpty());
|
||||
}
|
||||
|
||||
// --- Performance: O(numPts × numLabels) vs O(numPts) ---
|
||||
{
|
||||
int numPts = 500_000;
|
||||
int numLabels = 200;
|
||||
double[] regions = buildRegions(numPts, numLabels);
|
||||
|
||||
SlowCollect slow = new SlowCollect();
|
||||
FastCollect fast = new FastCollect();
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
List<Double> slowResult = slow.collect(regions);
|
||||
long slowNs = System.nanoTime() - t0;
|
||||
|
||||
t0 = System.nanoTime();
|
||||
List<Double> fastResult = fast.collect(regions);
|
||||
long fastNs = System.nanoTime() - t0;
|
||||
|
||||
// Slow scan count: once every label is seen (after first numLabels pts),
|
||||
// each subsequent point triggers a full numLabels scan → ≈ numPts × numLabels / 2
|
||||
long slowOps = slow.totalOps;
|
||||
long fastOps = fast.totalOps;
|
||||
double ratio = (double) slowNs / fastNs;
|
||||
|
||||
System.out.printf(" INFO numPts=%d numLabels=%d slow_ops=%d fast_ops=%d ratio=%.1fx%n",
|
||||
numPts, numLabels, slowOps, fastOps, ratio);
|
||||
|
||||
check("slow ops >> fast ops (>= 10x)", slowOps >= fastOps * 10);
|
||||
check("fast ops == numPts", fastOps == numPts);
|
||||
check("fast is meaningfully faster (>= 3x)", ratio >= 3.0);
|
||||
check("label counts agree", slowResult.size() == fastResult.size());
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.out.printf("%d/%d PASS%n", passed, total);
|
||||
if (passed != total) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue