130 lines
4.4 KiB
Java
130 lines
4.4 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* kafka-0006: DefaultTaskManager lockedTasks ArrayList → HashSet
|
||
*
|
||
* Demonstrates that ArrayList.contains() inside a task-scheduling loop is O(T×L)
|
||
* while HashSet.contains() is O(T).
|
||
*
|
||
* Compile: javac -d . KafkaStreamsDefaultTaskManagerTest.java
|
||
* Run: java unit.KafkaStreamsDefaultTaskManagerTest
|
||
*/
|
||
public class KafkaStreamsDefaultTaskManagerTest {
|
||
|
||
// Simulated TaskId (Integer wrapper for simplicity)
|
||
static class TaskId {
|
||
final int id;
|
||
TaskId(int id) { this.id = id; }
|
||
|
||
@Override public boolean equals(Object o) {
|
||
return o instanceof TaskId && ((TaskId) o).id == this.id;
|
||
}
|
||
@Override public int hashCode() { return Integer.hashCode(id); }
|
||
@Override public String toString() { return "Task-" + id; }
|
||
}
|
||
|
||
// --- SLOW: ArrayList lockedTasks (defective, mirrors DefaultTaskManager line 62) ---
|
||
static class DefectiveTaskManager {
|
||
private final List<TaskId> lockedTasks = new ArrayList<>();
|
||
private final List<TaskId> activeTasks;
|
||
|
||
DefectiveTaskManager(List<TaskId> activeTasks) {
|
||
this.activeTasks = activeTasks;
|
||
}
|
||
|
||
void lockTasks(Set<TaskId> ids) { lockedTasks.addAll(ids); }
|
||
|
||
// Returns count of assignable tasks — mirrors assignNextTask() loop body
|
||
int countAssignable() {
|
||
int count = 0;
|
||
for (TaskId task : activeTasks) {
|
||
if (!lockedTasks.contains(task)) { // O(L) ArrayList scan
|
||
count++;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
}
|
||
|
||
// --- FAST: HashSet lockedTasks (fixed) ---
|
||
static class FixedTaskManager {
|
||
private final Set<TaskId> lockedTasks = new HashSet<>();
|
||
private final List<TaskId> activeTasks;
|
||
|
||
FixedTaskManager(List<TaskId> activeTasks) {
|
||
this.activeTasks = activeTasks;
|
||
}
|
||
|
||
void lockTasks(Set<TaskId> ids) { lockedTasks.addAll(ids); }
|
||
|
||
int countAssignable() {
|
||
int count = 0;
|
||
for (TaskId task : activeTasks) {
|
||
if (!lockedTasks.contains(task)) { // O(1) HashSet lookup
|
||
count++;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
}
|
||
|
||
static long bench(Runnable r, int iters) {
|
||
// warmup
|
||
for (int i = 0; i < 3; i++) r.run();
|
||
long t0 = System.nanoTime();
|
||
for (int i = 0; i < iters; i++) r.run();
|
||
return System.nanoTime() - t0;
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("kafka-0006: DefaultTaskManager lockedTasks ArrayList → HashSet");
|
||
System.out.println("=".repeat(65));
|
||
|
||
int[] sizes = {100, 500, 1000};
|
||
int iters = 500;
|
||
boolean allPass = true;
|
||
|
||
for (int n : sizes) {
|
||
// Build n active tasks, lock half
|
||
List<TaskId> active = new ArrayList<>();
|
||
Set<TaskId> toLock = new HashSet<>();
|
||
for (int i = 0; i < n; i++) {
|
||
TaskId t = new TaskId(i);
|
||
active.add(t);
|
||
if (i % 2 == 0) toLock.add(t);
|
||
}
|
||
|
||
DefectiveTaskManager slow = new DefectiveTaskManager(active);
|
||
slow.lockTasks(toLoad(toLoad(toLock)));
|
||
FixedTaskManager fast = new FixedTaskManager(active);
|
||
fast.lockTasks(toLoad(toLock));
|
||
|
||
// Correctness check
|
||
int slowResult = slow.countAssignable();
|
||
int fastResult = fast.countAssignable();
|
||
boolean correct = (slowResult == fastResult) && (slowResult == n / 2);
|
||
if (!correct) allPass = false;
|
||
|
||
long slowNs = bench(slow::countAssignable, iters);
|
||
long fastNs = bench(fast::countAssignable, iters);
|
||
double ratio = (double) slowNs / fastNs;
|
||
|
||
System.out.printf("N=%-5d slow=%7.2f ms fast=%7.2f ms ratio=%.1fx assignable=%d %s%n",
|
||
n,
|
||
slowNs / 1_000_000.0 / iters,
|
||
fastNs / 1_000_000.0 / iters,
|
||
ratio,
|
||
slowResult,
|
||
correct ? "PASS" : "FAIL(result mismatch)");
|
||
}
|
||
|
||
System.out.println("=".repeat(65));
|
||
System.out.println(allPass ? "ALL PASS" : "SOME FAILED");
|
||
if (!allPass) System.exit(1);
|
||
}
|
||
|
||
// Helper: copy set into set (simulate addAll contract)
|
||
static Set<TaskId> toLoad(Set<TaskId> s) { return s; }
|
||
}
|