166 lines
5.8 KiB
Java
166 lines
5.8 KiB
Java
package unit;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* SM-0002: MDefinitionRemapper::lookup() O(N) linear scan inside loop-unroll cloning.
|
|
*
|
|
* Models:
|
|
* - SlowRemapper — Vector<Pair> linear lookup, O(V) per lookup → O(V²) per body clone
|
|
* - FastRemapper — HashMap<K,V> lookup, O(1) per lookup → O(V) per body clone
|
|
*
|
|
* Compile: javac -d . *.java (from defects/spidermonkey/unit/)
|
|
* Run: java -ea unit.SpiderMonkeyRemapperTest
|
|
*/
|
|
public class SpiderMonkeyRemapperTest {
|
|
|
|
// ---- slow path: Vector<Pair> linear scan --------------------------------
|
|
|
|
static class SlowRemapper {
|
|
static long opCount;
|
|
|
|
static class Pair {
|
|
final int original;
|
|
int replacement;
|
|
Pair(int o) { original = o; replacement = o; }
|
|
}
|
|
|
|
private final List<Pair> pairs = new ArrayList<>();
|
|
|
|
void enregister(int original) {
|
|
pairs.add(new Pair(original));
|
|
}
|
|
|
|
int lookup(int original) {
|
|
for (Pair p : pairs) {
|
|
opCount++;
|
|
if (p.original == original)
|
|
return p.replacement;
|
|
}
|
|
return -1; // not found
|
|
}
|
|
|
|
void update(int original, int replacement) {
|
|
for (Pair p : pairs) {
|
|
if (p.original == original) {
|
|
p.replacement = replacement;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---- fast path: HashMap O(1) lookup -------------------------------------
|
|
|
|
static class FastRemapper {
|
|
static long opCount;
|
|
private final HashMap<Integer, Integer> map = new HashMap<>();
|
|
|
|
void enregister(int original) {
|
|
map.put(original, original);
|
|
}
|
|
|
|
int lookup(int original) {
|
|
opCount++; // one hash lookup
|
|
Integer v = map.get(original);
|
|
return v == null ? -1 : v;
|
|
}
|
|
|
|
void update(int original, int replacement) {
|
|
map.put(original, replacement);
|
|
}
|
|
}
|
|
|
|
// ---- simulate loop body clone -------------------------------------------
|
|
// For each of V values in the mapper, clone K operands that each call lookup().
|
|
// Operands reference value (V-1) — the last registered — to hit worst-case
|
|
// scan length for SlowRemapper (must scan entire list every time).
|
|
|
|
static long simulateClone(boolean fast, int V, int K) {
|
|
int worstCaseKey = V - 1; // last in the vector → full scan each time
|
|
if (fast) {
|
|
FastRemapper.opCount = 0;
|
|
FastRemapper mapper = new FastRemapper();
|
|
for (int i = 0; i < V; i++) mapper.enregister(i);
|
|
// clone V instructions, each with K operands
|
|
for (int insn = 0; insn < V; insn++) {
|
|
for (int op = 0; op < K; op++) {
|
|
mapper.lookup(worstCaseKey);
|
|
}
|
|
}
|
|
return FastRemapper.opCount;
|
|
} else {
|
|
SlowRemapper.opCount = 0;
|
|
SlowRemapper mapper = new SlowRemapper();
|
|
for (int i = 0; i < V; i++) mapper.enregister(i);
|
|
for (int insn = 0; insn < V; insn++) {
|
|
for (int op = 0; op < K; op++) {
|
|
mapper.lookup(worstCaseKey);
|
|
}
|
|
}
|
|
return SlowRemapper.opCount;
|
|
}
|
|
}
|
|
|
|
// ---- tests --------------------------------------------------------------
|
|
|
|
static int passed = 0;
|
|
static int total = 0;
|
|
|
|
static void check(String name, boolean cond) {
|
|
total++;
|
|
if (cond) {
|
|
passed++;
|
|
} else {
|
|
System.out.println("FAIL: " + name);
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
// correctness: both return same result
|
|
{
|
|
SlowRemapper slow = new SlowRemapper();
|
|
FastRemapper fast = new FastRemapper();
|
|
int[] vals = {10, 20, 30, 40, 50};
|
|
for (int v : vals) { slow.enregister(v); fast.enregister(v); }
|
|
slow.update(20, 99); fast.update(20, 99);
|
|
check("slow lookup 10", slow.lookup(10) == 10);
|
|
check("fast lookup 10", fast.lookup(10) == 10);
|
|
check("slow lookup 20 updated", slow.lookup(20) == 99);
|
|
check("fast lookup 20 updated", fast.lookup(20) == 99);
|
|
check("slow lookup 50", slow.lookup(50) == 50);
|
|
check("fast lookup 50", fast.lookup(50) == 50);
|
|
check("slow lookup miss", slow.lookup(99) == -1);
|
|
check("fast lookup miss", fast.lookup(99) == -1);
|
|
}
|
|
|
|
// op-count scaling: V values, K=3 operands each
|
|
int[] sizes = {10, 30, 60, 100, 150};
|
|
int K = 3;
|
|
|
|
System.out.println();
|
|
System.out.printf("%-8s %12s %12s %8s%n", "V", "slow_ops", "fast_ops", "ratio");
|
|
for (int V : sizes) {
|
|
long slowOps = simulateClone(false, V, K);
|
|
long fastOps = simulateClone(true, V, K);
|
|
double ratio = (double) slowOps / fastOps;
|
|
System.out.printf("%-8d %12d %12d %8.1f%n", V, slowOps, fastOps, ratio);
|
|
|
|
// slow ops should be O(V²): roughly V * K * V/2 on average
|
|
// fast ops should be O(V): exactly V * K
|
|
long expectedFast = (long) V * K;
|
|
// slow worst-case: each lookup scans all V entries (key is last)
|
|
long expectedSlow = (long) V * K * V;
|
|
check("fast ops == V*K for V=" + V, fastOps == expectedFast);
|
|
check("slow ops == V*K*V for V=" + V, slowOps == expectedSlow);
|
|
// ratio should be exactly V (slow scans V items per lookup)
|
|
check("slow/fast ratio == V for V=" + V, Math.abs(ratio - V) < 0.01);
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.printf("%d/%d PASS%n", passed, total);
|
|
if (passed != total) System.exit(1);
|
|
}
|
|
}
|