wave15: v8-0002/0003 Intl+revectorizer (125x/25x) + bullet/box2d ticket files — 528/240
This commit is contained in:
parent
c4026333ba
commit
7146714143
13 changed files with 1520 additions and 11 deletions
331
defects/v8/unit/V8Algorithm.java
Normal file
331
defects/v8/unit/V8Algorithm.java
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* V8Algorithm
|
||||
*
|
||||
* Unit tests for two CWE-407 defects in V8:
|
||||
*
|
||||
* v8-0002: Intl::CanonicalizeLocaleList — src/objects/intl-objects.cc:940
|
||||
* seen is a std::vector<std::string>; each locale membership check
|
||||
* is O(seen.size()) → total O(N²) for N input locales.
|
||||
* Fix: parallel std::unordered_set for O(1) membership, keep vector
|
||||
* for ordered output (ECMA-402 spec requires insertion order).
|
||||
*
|
||||
* v8-0003: SLPTree::TryReduceLoadChain — src/compiler/revectorizer.cc:538
|
||||
* loads is ZoneVector<Node*>; inner-loop membership check is
|
||||
* std::find(loads.begin(), loads.end(), *it) → O(N) per step
|
||||
* → O(N² × L) total for N loads, L effect-chain length.
|
||||
* Fix: ZoneUnorderedSet<Node*> built once before the loops → O(L).
|
||||
*
|
||||
* No external dependencies. Run with:
|
||||
* javac -d . V8Algorithm.java && java -ea unit.V8Algorithm
|
||||
*/
|
||||
public class V8Algorithm {
|
||||
|
||||
// =======================================================================
|
||||
// v8-0002: CanonicalizeLocaleList deduplication
|
||||
// =======================================================================
|
||||
|
||||
/**
|
||||
* Defective: ArrayList + linear contains — mirrors std::vector::find on seen.
|
||||
* Returns the number of string comparisons performed (instrumented).
|
||||
*/
|
||||
static long canonicalizeLocaleListSlow(String[] inputLocales) {
|
||||
ArrayList<String> seen = new ArrayList<>();
|
||||
long comparisons = 0;
|
||||
for (String tag : inputLocales) {
|
||||
// O(seen.size()) linear scan — the defect
|
||||
boolean found = false;
|
||||
for (String existing : seen) {
|
||||
comparisons++;
|
||||
if (existing.equals(tag)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
seen.add(tag);
|
||||
}
|
||||
}
|
||||
return comparisons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed: insertion-order LinkedHashSet for O(1) contains, preserving spec order.
|
||||
* Returns the number of hash lookups performed (instrumented as 1 per input).
|
||||
*/
|
||||
static long canonicalizeLocaleListFast(String[] inputLocales) {
|
||||
LinkedHashSet<String> seen = new LinkedHashSet<>();
|
||||
long lookups = 0;
|
||||
for (String tag : inputLocales) {
|
||||
lookups++; // one O(1) hash lookup per input
|
||||
seen.add(tag); // no-op if already present
|
||||
}
|
||||
return lookups;
|
||||
}
|
||||
|
||||
/** Build a locale array: N entries cycling over `distinct` unique tags. */
|
||||
static String[] makeLocales(int n, int distinct) {
|
||||
String[] tags = new String[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
tags[i] = "locale-" + (i % distinct);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// v8-0003: TryReduceLoadChain membership test
|
||||
// =======================================================================
|
||||
|
||||
/**
|
||||
* Defective: List.contains (O(N)) inside inner loop — mirrors
|
||||
* std::find(loads.begin(), loads.end(), *it).
|
||||
*
|
||||
* Simulates: for each load (N), walk a chain of L steps; at each step
|
||||
* check membership with linear scan.
|
||||
*
|
||||
* Returns total comparison count.
|
||||
*/
|
||||
static long tryReduceLoadChainSlow(int numLoads, int chainLen) {
|
||||
List<Integer> loads = new ArrayList<>();
|
||||
for (int i = 0; i < numLoads; i++) loads.add(i);
|
||||
|
||||
long comparisons = 0;
|
||||
for (int loadIdx = 0; loadIdx < numLoads; loadIdx++) {
|
||||
// Walk the simulated effect chain
|
||||
for (int step = 0; step < chainLen; step++) {
|
||||
// Probe value cycles through loads to produce hits
|
||||
int probe = step % numLoads;
|
||||
// O(N) linear scan — the defect
|
||||
for (int j = 0; j < loads.size(); j++) {
|
||||
comparisons++;
|
||||
if (loads.get(j).equals(probe)) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return comparisons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed: HashSet built once; O(1) contains in inner loop.
|
||||
* Returns total lookup count.
|
||||
*/
|
||||
static long tryReduceLoadChainFast(int numLoads, int chainLen) {
|
||||
List<Integer> loads = new ArrayList<>();
|
||||
for (int i = 0; i < numLoads; i++) loads.add(i);
|
||||
|
||||
HashSet<Integer> loadsSet = new HashSet<>(loads); // built once: O(N)
|
||||
long lookups = 0;
|
||||
for (int loadIdx = 0; loadIdx < numLoads; loadIdx++) {
|
||||
for (int step = 0; step < chainLen; step++) {
|
||||
int probe = step % numLoads;
|
||||
lookups++; // O(1) hash lookup — the fix
|
||||
loadsSet.contains(probe);
|
||||
}
|
||||
}
|
||||
return lookups;
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// Tests — v8-0002
|
||||
// =======================================================================
|
||||
|
||||
static void test1_intl_correctness() {
|
||||
// Both paths must return the same deduplication result.
|
||||
int n = 20, distinct = 5;
|
||||
String[] locales = makeLocales(n, distinct);
|
||||
|
||||
ArrayList<String> slowResult = new ArrayList<>();
|
||||
for (String tag : locales) if (!slowResult.contains(tag)) slowResult.add(tag);
|
||||
|
||||
LinkedHashSet<String> fastResult = new LinkedHashSet<>();
|
||||
for (String tag : locales) fastResult.add(tag);
|
||||
|
||||
assert slowResult.size() == fastResult.size()
|
||||
: "result sizes differ: slow=" + slowResult.size() + " fast=" + fastResult.size();
|
||||
assert slowResult.equals(new ArrayList<>(fastResult))
|
||||
: "result order differs";
|
||||
|
||||
System.out.printf(
|
||||
"test1 [v8-0002 correctness]: n=%d distinct=%d slow_size=%d fast_size=%d ORDER_PRESERVED%n",
|
||||
n, distinct, slowResult.size(), fastResult.size());
|
||||
}
|
||||
|
||||
static void test2_intl_ratio_at_n500() {
|
||||
int n = 500, distinct = 250;
|
||||
String[] locales = makeLocales(n, distinct);
|
||||
long slow = canonicalizeLocaleListSlow(locales);
|
||||
long fast = canonicalizeLocaleListFast(locales);
|
||||
double ratio = (double) slow / Math.max(1, fast);
|
||||
|
||||
System.out.printf(
|
||||
"test2 [v8-0002 ratio N=500]: slow_comparisons=%d fast_lookups=%d ratio=%.1fx%n",
|
||||
slow, fast, ratio);
|
||||
|
||||
assert ratio >= 5.0
|
||||
: "expected ratio >= 5x at N=500, got " + ratio;
|
||||
}
|
||||
|
||||
static void test3_intl_all_distinct() {
|
||||
int n = 500;
|
||||
String[] locales = makeLocales(n, n); // all unique
|
||||
long slow = canonicalizeLocaleListSlow(locales);
|
||||
long fast = canonicalizeLocaleListFast(locales);
|
||||
|
||||
// All distinct: slow = 0+1+2+...+(n-1) = n*(n-1)/2
|
||||
long expectedSlow = (long) n * (n - 1) / 2;
|
||||
double ratio = (double) slow / Math.max(1, fast);
|
||||
|
||||
System.out.printf(
|
||||
"test3 [v8-0002 all-distinct N=500]: slow=%d (expect=%d) fast=%d ratio=%.1fx%n",
|
||||
slow, expectedSlow, fast, ratio);
|
||||
|
||||
assert slow == expectedSlow
|
||||
: "slow comparisons=" + slow + " expected=" + expectedSlow;
|
||||
assert ratio >= 100.0
|
||||
: "expected ratio >= 100x for all-distinct N=500, got " + ratio;
|
||||
}
|
||||
|
||||
static void test4_intl_scaling() {
|
||||
// Doubling N (all distinct) should quadruple slow ops (O(N²)), double fast ops (O(N)).
|
||||
int n1 = 200, n2 = 400;
|
||||
// All distinct to get clean O(N²) vs O(N) growth
|
||||
long s1 = canonicalizeLocaleListSlow(makeLocales(n1, n1));
|
||||
long s2 = canonicalizeLocaleListSlow(makeLocales(n2, n2));
|
||||
long f1 = canonicalizeLocaleListFast(makeLocales(n1, n1));
|
||||
long f2 = canonicalizeLocaleListFast(makeLocales(n2, n2));
|
||||
|
||||
double slowGrowth = (double) s2 / Math.max(1, s1);
|
||||
double fastGrowth = (double) f2 / Math.max(1, f1);
|
||||
|
||||
System.out.printf(
|
||||
"test4 [v8-0002 scaling all-distinct]: slow 2x_N growth=%.2fx fast growth=%.2fx%n",
|
||||
slowGrowth, fastGrowth);
|
||||
|
||||
// O(N²): doubling N → ~4x ops; threshold 3x to allow small N effects
|
||||
assert slowGrowth > 3.0
|
||||
: "slow should grow ~quadratically on 2x N (all-distinct), got " + slowGrowth;
|
||||
assert fastGrowth <= 2.5
|
||||
: "fast should grow at most linearly on 2x N, got " + fastGrowth;
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// Tests — v8-0003
|
||||
// =======================================================================
|
||||
|
||||
static void test5_revec_ratio_n16_chain100() {
|
||||
int n = 16, chainLen = 100;
|
||||
long slow = tryReduceLoadChainSlow(n, chainLen);
|
||||
long fast = tryReduceLoadChainFast(n, chainLen);
|
||||
double ratio = (double) slow / Math.max(1, fast);
|
||||
|
||||
System.out.printf(
|
||||
"test5 [v8-0003 ratio N=16 L=100]: slow=%d fast=%d ratio=%.1fx%n",
|
||||
slow, fast, ratio);
|
||||
|
||||
assert ratio >= 5.0
|
||||
: "expected ratio >= 5x at N=16 L=100, got " + ratio;
|
||||
}
|
||||
|
||||
static void test6_revec_ratio_n64_chain50() {
|
||||
int n = 64, chainLen = 50;
|
||||
long slow = tryReduceLoadChainSlow(n, chainLen);
|
||||
long fast = tryReduceLoadChainFast(n, chainLen);
|
||||
double ratio = (double) slow / Math.max(1, fast);
|
||||
|
||||
System.out.printf(
|
||||
"test6 [v8-0003 ratio N=64 L=50]: slow=%d fast=%d ratio=%.1fx%n",
|
||||
slow, fast, ratio);
|
||||
|
||||
assert ratio >= 20.0
|
||||
: "expected ratio >= 20x at N=64 L=50, got " + ratio;
|
||||
}
|
||||
|
||||
static void test7_revec_scaling() {
|
||||
// Doubling N should roughly quadruple slow, double fast.
|
||||
int chainLen = 50;
|
||||
int n1 = 32, n2 = 64;
|
||||
long s1 = tryReduceLoadChainSlow(n1, chainLen);
|
||||
long s2 = tryReduceLoadChainSlow(n2, chainLen);
|
||||
long f1 = tryReduceLoadChainFast(n1, chainLen);
|
||||
long f2 = tryReduceLoadChainFast(n2, chainLen);
|
||||
|
||||
double slowGrowth = (double) s2 / Math.max(1, s1);
|
||||
double fastGrowth = (double) f2 / Math.max(1, f1);
|
||||
|
||||
System.out.printf(
|
||||
"test7 [v8-0003 scaling]: slow 2x_N growth=%.2fx fast growth=%.2fx%n",
|
||||
slowGrowth, fastGrowth);
|
||||
|
||||
assert slowGrowth > 2.5
|
||||
: "slow should grow super-linearly on 2x N, got " + slowGrowth;
|
||||
assert fastGrowth <= 3.0
|
||||
: "fast should grow at most linearly on 2x N, got " + fastGrowth;
|
||||
}
|
||||
|
||||
static void test8_revec_correctness() {
|
||||
// Slow and fast paths should agree on which probes are in the set.
|
||||
int n = 20, chainLen = 30;
|
||||
// Re-implement logic to check that both paths would accept the same elements.
|
||||
List<Integer> loads = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) loads.add(i);
|
||||
HashSet<Integer> fastSet = new HashSet<>(loads);
|
||||
|
||||
long slowHits = 0, fastHits = 0;
|
||||
for (int step = 0; step < chainLen; step++) {
|
||||
int probe = step % n;
|
||||
if (loads.contains(probe)) slowHits++;
|
||||
if (fastSet.contains(probe)) fastHits++;
|
||||
}
|
||||
|
||||
System.out.printf(
|
||||
"test8 [v8-0003 correctness]: slow_hits=%d fast_hits=%d%n",
|
||||
slowHits, fastHits);
|
||||
|
||||
assert slowHits == fastHits
|
||||
: "slow and fast membership results disagree: slow=" + slowHits + " fast=" + fastHits;
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// Main
|
||||
// =======================================================================
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== V8Algorithm — CWE-407 unit tests ===");
|
||||
System.out.println(" v8-0002: Intl::CanonicalizeLocaleList (intl-objects.cc:940)");
|
||||
System.out.println(" v8-0003: SLPTree::TryReduceLoadChain (revectorizer.cc:538)");
|
||||
System.out.println();
|
||||
|
||||
test1_intl_correctness();
|
||||
System.out.println(" PASS test1_intl_correctness");
|
||||
|
||||
test2_intl_ratio_at_n500();
|
||||
System.out.println(" PASS test2_intl_ratio_at_n500");
|
||||
|
||||
test3_intl_all_distinct();
|
||||
System.out.println(" PASS test3_intl_all_distinct");
|
||||
|
||||
test4_intl_scaling();
|
||||
System.out.println(" PASS test4_intl_scaling");
|
||||
|
||||
test5_revec_ratio_n16_chain100();
|
||||
System.out.println(" PASS test5_revec_ratio_n16_chain100");
|
||||
|
||||
test6_revec_ratio_n64_chain50();
|
||||
System.out.println(" PASS test6_revec_ratio_n64_chain50");
|
||||
|
||||
test7_revec_scaling();
|
||||
System.out.println(" PASS test7_revec_scaling");
|
||||
|
||||
test8_revec_correctness();
|
||||
System.out.println(" PASS test8_revec_correctness");
|
||||
|
||||
System.out.println();
|
||||
System.out.println("8/8 PASS");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue