132 lines
5.3 KiB
Java
132 lines
5.3 KiB
Java
package unit;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* JuliaBackedgeDedupTest -- julia-0003: _typename_add_backedge() O(N²) linear scan
|
|
*
|
|
* Models the defect in Julia src/gf.c _typename_add_backedge() (lines 2376-2398):
|
|
* two full O(N) linear scans over the per-typename backedges array to:
|
|
* 1) detect exact duplicates (same caller + same type)
|
|
* 2) find a type-equal entry for pointer sharing
|
|
*
|
|
* Each insertion is O(N); adding N backedges costs O(N²) total.
|
|
* The TODO comment at gf.c:2379 confirms this is a known limitation.
|
|
*
|
|
* Fix: HashMap keyed on caller→Set<type> for O(1) dedup; identity-compare
|
|
* replaces jl_types_equal for the pointer-sharing pass using jl_cache_type_().
|
|
*
|
|
* Real code (src/gf.c:2381-2398):
|
|
* for (i = 1; i < l; i += 2) { // pass 1: dup check — O(N)
|
|
* if (entries[i] == caller &&
|
|
* jl_types_equal(entries[i-1], typ)) return;
|
|
* }
|
|
* for (i = 1; i < l; i += 2) { // pass 2: ptr sharing — O(N)
|
|
* if (entries[i] != caller &&
|
|
* jl_types_equal(entries[i-1], typ)) { typ = entries[i-1]; break; }
|
|
* }
|
|
*/
|
|
public class JuliaBackedgeDedupTest {
|
|
|
|
// Defective: O(N) per insertion (two linear scans)
|
|
static long slowAddEdge(List<Object> backedges, Object typ, Object caller) {
|
|
long ops = 0;
|
|
int l = backedges.size();
|
|
// Pass 1: exact dup check
|
|
for (int i = 1; i < l; i += 2) {
|
|
ops++;
|
|
if (backedges.get(i) == caller && backedges.get(i - 1).equals(typ)) {
|
|
return ops; // already recorded
|
|
}
|
|
}
|
|
// Pass 2: type pointer sharing
|
|
for (int i = 1; i < l; i += 2) {
|
|
ops++;
|
|
if (backedges.get(i) != caller && backedges.get(i - 1).equals(typ)) {
|
|
typ = backedges.get(i - 1);
|
|
break;
|
|
}
|
|
}
|
|
backedges.add(typ);
|
|
backedges.add(caller);
|
|
return ops;
|
|
}
|
|
|
|
// Fixed: O(1) per insertion (hash dedup)
|
|
static long fastAddEdge(List<Object> backedges,
|
|
Map<Object, Set<Object>> dedupIndex,
|
|
Map<Object, Object> typCache,
|
|
Object typ, Object caller) {
|
|
long ops = 1;
|
|
// Canonicalize type pointer
|
|
typ = typCache.computeIfAbsent(typ, k -> k);
|
|
// O(1) dup check
|
|
if (dedupIndex.computeIfAbsent(caller, k -> new HashSet<>()).add(typ)) {
|
|
backedges.add(typ);
|
|
backedges.add(caller);
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
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 speedup = fMs > 0 ? (double) sMs / fMs : 0;
|
|
System.out.printf(" %-60s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
|
|
label, sMs, sOps, fMs, fOps, speedup);
|
|
}
|
|
|
|
static long runSlow(int N) {
|
|
List<Object> backedges = new ArrayList<>();
|
|
String caller = "callerA";
|
|
long ops = 0;
|
|
for (int i = 0; i < N; i++) {
|
|
ops += slowAddEdge(backedges, "Type" + i, caller);
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
static long runFast(int N) {
|
|
List<Object> backedges = new ArrayList<>();
|
|
Map<Object, Set<Object>> dedupIndex = new HashMap<>();
|
|
Map<Object, Object> typCache = new HashMap<>();
|
|
String caller = "callerA";
|
|
long ops = 0;
|
|
for (int i = 0; i < N; i++) {
|
|
ops += fastAddEdge(backedges, dedupIndex, typCache, "Type" + i, caller);
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("JuliaBackedgeDedupTest -- julia-0003: _typename_add_backedge() O(N²) linear scan");
|
|
System.out.println();
|
|
System.out.println(" [backedge dedup: src/gf.c:2376-2398]");
|
|
int[] cases = {100, 250, 500};
|
|
for (int N : cases) {
|
|
long sOps = (long) N * N; // approx two passes each time
|
|
long fOps = N;
|
|
bench(
|
|
String.format("N=%d backedge insertions (single caller)", N),
|
|
() -> runSlow(N),
|
|
() -> runFast(N),
|
|
sOps, fOps
|
|
);
|
|
}
|
|
System.out.println();
|
|
System.out.println("Defect : src/gf.c:2381-2396 -- two O(N) scans per backedge insertion");
|
|
System.out.println("Fix : HashMap<caller, HashSet<typ>> for O(1) dedup; jl_cache_type_() for ptr sharing");
|
|
System.out.println("Note : TODO comment at gf.c:2379 explicitly flags this defect");
|
|
System.out.println("Ticket : julia-0003-typename-backedge-dedup-linear-scan.md");
|
|
System.out.println();
|
|
|
|
int pass = 0;
|
|
long s0 = runSlow(500), f0 = runFast(500);
|
|
assert s0 > f0 * 50 : "julia-0003 expected >50x op ratio; slow=" + s0 + " fast=" + f0;
|
|
pass++;
|
|
System.out.printf("%d/1 PASS -- julia-0003: CWE-407 in Julia _typename_add_backedge() backedge dedup%n", pass);
|
|
System.out.printf("Hotpath: called for every compilation of a method invoked on an abstract type%n");
|
|
System.out.printf("Op ratio at N=500: slow=%,d fast=%,d ratio=%.0fx%n", s0, f0, (double) s0 / f0);
|
|
}
|
|
}
|