java-topology/defects/spark/unit/MasterCompletedAppsAlgorithm.java

128 lines
4.4 KiB
Java

package unit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Unit test for spark-0003: Master.completedApps ArrayBuffer.contains() O(n²) on worker failure.
*
* Standalone — no JUnit. Run: javac -d . MasterCompletedAppsAlgorithm.java && java -ea unit.MasterCompletedAppsAlgorithm
*/
public class MasterCompletedAppsAlgorithm {
// ---------- Slow: ArrayList.contains inside filterNot ----------
static long slowFilterNot(Set<Integer> apps, List<Integer> completedApps) {
long ops = 0;
List<Integer> runningApps = new ArrayList<>();
for (Integer app : apps) {
ops += completedApps.size(); // O(size) per contains
if (!completedApps.contains(app)) {
runningApps.add(app);
}
}
return ops;
}
// ---------- Fast: HashSet.contains O(1) ----------
static long fastFilterNot(Set<Integer> apps, Set<Integer> completedAppsSet) {
long ops = 0;
List<Integer> runningApps = new ArrayList<>();
for (Integer app : apps) {
ops += 1; // O(1) per contains
if (!completedAppsSet.contains(app)) {
runningApps.add(app);
}
}
return ops;
}
static void testCorrectness() {
int A = 50; // running apps
int C = 20; // completed apps
Set<Integer> apps = new HashSet<>();
for (int i = 0; i < A + C; i++) apps.add(i);
List<Integer> completedList = new ArrayList<>();
for (int i = A; i < A + C; i++) completedList.add(i);
Set<Integer> completedSet = new HashSet<>(completedList);
// Compute running apps both ways
Set<Integer> slowRunning = new HashSet<>();
for (Integer app : apps) {
if (!completedList.contains(app)) slowRunning.add(app);
}
Set<Integer> fastRunning = new HashSet<>();
for (Integer app : apps) {
if (!completedSet.contains(app)) fastRunning.add(app);
}
assert slowRunning.equals(fastRunning) :
"Result mismatch: slow=" + slowRunning.size() + " fast=" + fastRunning.size();
assert slowRunning.size() == A :
"Expected " + A + " running apps, got " + slowRunning.size();
System.out.println("PASS: correctness — filterNot produces same " + A + " running apps");
}
static void testPerformance() {
int A = 100; // running apps
int C = 200; // completed apps (default retainedApplications)
Set<Integer> apps = new HashSet<>();
for (int i = 0; i < A + C; i++) apps.add(i);
List<Integer> completedList = new ArrayList<>();
Set<Integer> completedSet = new HashSet<>();
for (int i = A; i < A + C; i++) {
completedList.add(i);
completedSet.add(i);
}
long slowOps = slowFilterNot(apps, completedList);
long fastOps = fastFilterNot(apps, completedSet);
double ratio = (double) slowOps / fastOps;
System.out.printf("filterNot A=%d C=%d: slow_ops=%,d fast_ops=%,d ratio=%.1fx%n",
A, C, slowOps, fastOps, ratio);
assert ratio >= 10.0 :
"Expected >=10x speedup, got ratio=" + ratio;
System.out.println("PASS: performance — ratio >= 10x");
}
static void testLargeRetainedApps() {
// Stress test: many completed apps retained
int A = 500;
int C = 1000;
Set<Integer> apps = new HashSet<>();
for (int i = 0; i < A + C; i++) apps.add(i);
List<Integer> completedList = new ArrayList<>();
Set<Integer> completedSet = new HashSet<>();
for (int i = A; i < A + C; i++) {
completedList.add(i);
completedSet.add(i);
}
long slowOps = slowFilterNot(apps, completedList);
long fastOps = fastFilterNot(apps, completedSet);
double ratio = (double) slowOps / fastOps;
System.out.printf("filterNot A=%d C=%d: slow_ops=%,d fast_ops=%,d ratio=%.1fx%n",
A, C, slowOps, fastOps, ratio);
assert ratio >= 100.0 :
"Expected >=100x speedup, got ratio=" + ratio;
System.out.println("PASS: large retained — ratio >= 100x");
}
public static void main(String[] args) {
testCorrectness();
testPerformance();
testLargeRetainedApps();
System.out.println("ALL PASS (3/3)");
}
}