wave9 complete: 458/207 — airflow/argo/pytorch-0003/tf/jax/pyg/grpc/thrift + graphhopper/valhalla
This commit is contained in:
parent
81bc62b9cb
commit
ab10b2f555
22 changed files with 2889 additions and 16 deletions
245
defects/argo-workflows/unit/ArgoGetTaskAlgorithm.java
Normal file
245
defects/argo-workflows/unit/ArgoGetTaskAlgorithm.java
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* argo-0001 — O(N) linear GetTask() scan inside O(N) DAG execution loop
|
||||
*
|
||||
* Simulates the defective GetTask() from:
|
||||
* workflow/controller/dag.go:83-89
|
||||
*
|
||||
* Defect: dagContext stores tasks as []DAGTask (slice). GetTask(name) does
|
||||
* linear scan. Called O(N) times per executeDAG() cycle → O(N²) total.
|
||||
*
|
||||
* Fix: Build a map[string]*DAGTask at dagContext construction time → O(1) lookup.
|
||||
*
|
||||
* The unit test simulates:
|
||||
* 1. A DAGContext with N tasks (stored as list in defective, map in fixed)
|
||||
* 2. An executeDAG loop that calls GetTask() once per targetTask
|
||||
* 3. Counts total comparisons to confirm O(N²) vs O(N)
|
||||
*/
|
||||
public class ArgoGetTaskAlgorithm {
|
||||
|
||||
static int opCount = 0;
|
||||
|
||||
static void check(String desc, boolean cond) {
|
||||
System.out.println((cond ? "PASS" : "FAIL") + ": " + desc);
|
||||
if (!cond) throw new AssertionError("FAIL: " + desc);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Simulated DAGTask
|
||||
// -----------------------------------------------------------------------
|
||||
static class DAGTask {
|
||||
final String name;
|
||||
final List<String> dependencies;
|
||||
|
||||
DAGTask(String name, List<String> deps) {
|
||||
this.name = name;
|
||||
this.dependencies = deps;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// DEFECTIVE: tasks stored as List<DAGTask>, GetTask does linear scan
|
||||
// -----------------------------------------------------------------------
|
||||
static class DefectiveDagContext {
|
||||
final List<DAGTask> tasks;
|
||||
|
||||
DefectiveDagContext(List<DAGTask> tasks) {
|
||||
this.tasks = new ArrayList<>(tasks);
|
||||
}
|
||||
|
||||
DAGTask getTask(String taskName) {
|
||||
for (DAGTask task : tasks) { // O(N) scan
|
||||
opCount++;
|
||||
if (task.name.equals(taskName)) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("task not found: " + taskName);
|
||||
}
|
||||
|
||||
// Simulates executeDAG: for each target task, call getTask() 3 times
|
||||
// (matches the 3 GetTask calls on lines 292, 300, 307 in dag.go)
|
||||
void executeDAG(List<String> targetTasks) {
|
||||
for (String taskName : targetTasks) {
|
||||
getTask(taskName); // line 292: task := dagCtx.GetTask(ctx, taskName)
|
||||
getTask(taskName); // line 300: dagCtx.GetTask(ctx, taskName).Hooks
|
||||
getTask(taskName); // line 307: dagCtx.GetTask(ctx, taskName).GetExitHook(...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// FIXED: tasks stored as Map<String, DAGTask>, GetTask does O(1) lookup
|
||||
// -----------------------------------------------------------------------
|
||||
static class FixedDagContext {
|
||||
final List<DAGTask> tasks;
|
||||
final Map<String, DAGTask> taskByName;
|
||||
|
||||
FixedDagContext(List<DAGTask> tasks) {
|
||||
this.tasks = new ArrayList<>(tasks);
|
||||
this.taskByName = new HashMap<>(tasks.size() * 2);
|
||||
for (DAGTask t : tasks) {
|
||||
this.taskByName.put(t.name, t);
|
||||
}
|
||||
}
|
||||
|
||||
DAGTask getTask(String taskName) {
|
||||
opCount++; // count each lookup
|
||||
DAGTask task = taskByName.get(taskName);
|
||||
if (task == null) throw new RuntimeException("task not found: " + taskName);
|
||||
return task;
|
||||
}
|
||||
|
||||
void executeDAG(List<String> targetTasks) {
|
||||
for (String taskName : targetTasks) {
|
||||
getTask(taskName);
|
||||
getTask(taskName);
|
||||
getTask(taskName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build N tasks (all as target tasks — worst case: getTask scans entire list)
|
||||
static List<DAGTask> buildTasks(int n) {
|
||||
List<DAGTask> tasks = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
tasks.add(new DAGTask("task-" + i, Collections.emptyList()));
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 1: Correctness — both return same task
|
||||
// ------------------------------------------------------------------
|
||||
total++;
|
||||
{
|
||||
List<DAGTask> tasks = buildTasks(10);
|
||||
DefectiveDagContext def = new DefectiveDagContext(tasks);
|
||||
FixedDagContext fix = new FixedDagContext(tasks);
|
||||
|
||||
opCount = 0;
|
||||
DAGTask defTask = def.getTask("task-7");
|
||||
opCount = 0;
|
||||
DAGTask fixTask = fix.getTask("task-7");
|
||||
|
||||
check("Correctness: both find task-7", defTask.name.equals("task-7") && fixTask.name.equals("task-7"));
|
||||
passed++;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 2: Op-count for getTask on last element — defective is O(N)
|
||||
// ------------------------------------------------------------------
|
||||
total++;
|
||||
{
|
||||
int n = 100;
|
||||
List<DAGTask> tasks = buildTasks(n);
|
||||
DefectiveDagContext def = new DefectiveDagContext(tasks);
|
||||
FixedDagContext fix = new FixedDagContext(tasks);
|
||||
|
||||
opCount = 0;
|
||||
def.getTask("task-99"); // last element -> worst case: N comparisons
|
||||
long defOps = opCount;
|
||||
|
||||
opCount = 0;
|
||||
fix.getTask("task-99");
|
||||
long fixOps = opCount;
|
||||
|
||||
System.out.printf(" Single getTask (last of N=%d): defective=%d ops, fixed=%d ops%n",
|
||||
n, defOps, fixOps);
|
||||
check("Defective getTask(last) scans all N elements", defOps == n);
|
||||
check("Fixed getTask is O(1)", fixOps == 1);
|
||||
passed += 2;
|
||||
total++;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 3: executeDAG op-count ratio — O(N²) vs O(N)
|
||||
// ------------------------------------------------------------------
|
||||
total++;
|
||||
{
|
||||
int n = 300;
|
||||
List<DAGTask> tasks = buildTasks(n);
|
||||
List<String> targetTasks = new ArrayList<>();
|
||||
for (DAGTask t : tasks) targetTasks.add(t.name);
|
||||
|
||||
DefectiveDagContext def = new DefectiveDagContext(tasks);
|
||||
FixedDagContext fix = new FixedDagContext(tasks);
|
||||
|
||||
opCount = 0;
|
||||
def.executeDAG(targetTasks);
|
||||
long defOps = opCount;
|
||||
|
||||
opCount = 0;
|
||||
fix.executeDAG(targetTasks);
|
||||
long fixOps = opCount;
|
||||
|
||||
double ratio = (double) defOps / fixOps;
|
||||
System.out.printf(" executeDAG N=%d: defective=%d ops, fixed=%d ops, ratio=%.1fx%n",
|
||||
n, defOps, fixOps, ratio);
|
||||
// Defective: 3 calls × avg N/2 scans × N tasks = 3N²/2
|
||||
// Fixed: 3 calls × 1 lookup × N tasks = 3N
|
||||
// Expected ratio ≈ N/2 = 150 for N=300; require >= 50x conservatively
|
||||
check("Op-count ratio >= 50x for executeDAG N=" + n, ratio >= 50.0);
|
||||
passed++;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 4: Quadratic growth — ops at N=200 should be ~4x ops at N=100
|
||||
// ------------------------------------------------------------------
|
||||
total++;
|
||||
{
|
||||
List<DAGTask> tasks100 = buildTasks(100);
|
||||
List<DAGTask> tasks200 = buildTasks(200);
|
||||
List<String> target100 = new ArrayList<>(); for (DAGTask t : tasks100) target100.add(t.name);
|
||||
List<String> target200 = new ArrayList<>(); for (DAGTask t : tasks200) target200.add(t.name);
|
||||
|
||||
opCount = 0;
|
||||
new DefectiveDagContext(tasks100).executeDAG(target100);
|
||||
long ops100 = opCount;
|
||||
|
||||
opCount = 0;
|
||||
new DefectiveDagContext(tasks200).executeDAG(target200);
|
||||
long ops200 = opCount;
|
||||
|
||||
double growthRatio = (double) ops200 / ops100;
|
||||
System.out.printf(" Defective growth (200 vs 100): %.2fx (expected ~4x for O(N²))%n",
|
||||
growthRatio);
|
||||
check("Defective shows quadratic growth (ratio >= 3.5x)", growthRatio >= 3.5);
|
||||
passed++;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 5: Linear growth for fixed
|
||||
// ------------------------------------------------------------------
|
||||
total++;
|
||||
{
|
||||
List<DAGTask> tasks100 = buildTasks(100);
|
||||
List<DAGTask> tasks200 = buildTasks(200);
|
||||
List<String> target100 = new ArrayList<>(); for (DAGTask t : tasks100) target100.add(t.name);
|
||||
List<String> target200 = new ArrayList<>(); for (DAGTask t : tasks200) target200.add(t.name);
|
||||
|
||||
opCount = 0;
|
||||
new FixedDagContext(tasks100).executeDAG(target100);
|
||||
long ops100 = opCount;
|
||||
|
||||
opCount = 0;
|
||||
new FixedDagContext(tasks200).executeDAG(target200);
|
||||
long ops200 = opCount;
|
||||
|
||||
double growthRatio = (double) ops200 / ops100;
|
||||
System.out.printf(" Fixed growth (200 vs 100): %.2fx (expected ~2x for O(N))%n",
|
||||
growthRatio);
|
||||
check("Fixed shows linear growth (ratio < 3.0x)", growthRatio < 3.0);
|
||||
passed++;
|
||||
}
|
||||
|
||||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue