java-topology/defects/kafka/unit/KafkaListDeserializerNullIndexTest.java

105 lines
3.1 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* kafka-0008 — ListDeserializer nullIndexList ArrayList.contains O(S×N) → HashSet O(S)
*
* Simulates the inner deserialization loop:
* for (int i = 0; i < size; i++) {
* if (nullIndexList.contains(i)) { ... }
* }
*
* SLOW: nullIndexList is ArrayList<Integer> → O(S × N)
* FAST: nullIndexList is HashSet<Integer> → O(S)
*/
public class KafkaListDeserializerNullIndexTest {
static long slowContains(int size, List<Integer> nullIndexList) {
long ops = 0;
for (int i = 0; i < size; i++) {
ops++;
if (nullIndexList.contains(i)) {
// null entry — just count
}
}
return ops;
}
static long fastContains(int size, Set<Integer> nullIndexSet) {
long ops = 0;
for (int i = 0; i < size; i++) {
ops++;
if (nullIndexSet.contains(i)) {
// null entry — just count
}
}
return ops;
}
static long benchSlow(int size, int nullCount) {
List<Integer> nullIndexList = new ArrayList<>(nullCount);
// nulls at every-other index (worst-case spread)
for (int i = 0; i < nullCount; i++) {
nullIndexList.add(i * 2);
}
long start = System.nanoTime();
long ops = slowContains(size, nullIndexList);
long elapsed = System.nanoTime() - start;
return elapsed;
}
static long benchFast(int size, int nullCount) {
Set<Integer> nullIndexSet = new HashSet<>(nullCount * 2);
for (int i = 0; i < nullCount; i++) {
nullIndexSet.add(i * 2);
}
long start = System.nanoTime();
long ops = fastContains(size, nullIndexSet);
long elapsed = System.nanoTime() - start;
return elapsed;
}
public static void main(String[] args) {
int passed = 0;
int failed = 0;
int minRatio = 5;
int[][] cases = {
{1000, 500},
{2000, 1000},
{5000, 2500},
};
// warmup
benchSlow(500, 250);
benchFast(500, 250);
for (int[] c : cases) {
int size = c[0];
int nullCount = c[1];
// run multiple times for stable timing
long slowTotal = 0, fastTotal = 0;
int reps = 5;
for (int r = 0; r < reps; r++) {
slowTotal += benchSlow(size, nullCount);
fastTotal += benchFast(size, nullCount);
}
long slowAvg = slowTotal / reps;
long fastAvg = fastTotal / reps;
double ratio = fastAvg > 0 ? (double) slowAvg / fastAvg : 999.0;
boolean ok = ratio >= minRatio;
System.out.printf(" size=%-5d nulls=%-5d slow=%7dns fast=%7dns ratio=%.1fx %s%n",
size, nullCount, slowAvg, fastAvg, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++; else failed++;
}
System.out.printf("%nTotal: %d/%d PASS%n", passed, passed + failed);
if (failed > 0) System.exit(1);
}
}