libreoffice-0001: SavePivotTableXml member-to-cache O(M*C) std::find sc/source/filter/excel/xepivotxml.cxx SavePivotTableXml() for (member : aMembers) std::find(aCacheFieldItems) -> O(M*C) Fix: unordered_map<OUString,size_t> index built once -> O(M+C) Measured: 7.1x speedup at M=C=2000 calligra-0001: KoShapeManager addShape QList::contains O(N^2) dedup libs/flake/KoShapeManager.cpp addShape() called from setShapes() loop QList<KoShape*>::contains is O(N); N calls = O(N^2) total Fix: change d->shapes to QSet<KoShape*> for O(1) membership Measured: 12.6x speedup at N=5000 MOADs 0002-0005: CLEAN for both targets (see README.md per defect) Both unit tests: PASS
106 lines
3.8 KiB
Java
106 lines
3.8 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* Unit test for calligra-0001: KoShapeManager addShape O(N^2) membership check.
|
|
*
|
|
* Defect: libs/flake/KoShapeManager.cpp addShape()
|
|
* if (d->shapes.contains(shape)) // QList::contains is O(N)
|
|
* d->shapes.append(shape);
|
|
*
|
|
* Called from setShapes() in a loop over N shapes, producing O(N^2) total.
|
|
*
|
|
* Fix: change d->shapes from QList to QSet, making contains() O(1).
|
|
*
|
|
* Complexity: O(N^2) -> O(N).
|
|
*/
|
|
public class CalligraShapeManagerAddTest {
|
|
|
|
// --- Defective implementation: QList equivalent ---
|
|
static List<Integer> buildShapeListLinear(int[] shapes) {
|
|
List<Integer> result = new ArrayList<>();
|
|
for (int shape : shapes) {
|
|
if (!result.contains(shape)) { // O(N) each
|
|
result.add(shape);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// --- Fixed implementation: QSet equivalent ---
|
|
static Set<Integer> buildShapeSetFixed(int[] shapes) {
|
|
Set<Integer> result = new LinkedHashSet<>();
|
|
for (int shape : shapes) {
|
|
result.add(shape); // O(1) each, set deduplicates automatically
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// --- Benchmark ---
|
|
static long benchmarkLinear(int N) {
|
|
int[] shapes = new int[N];
|
|
for (int i = 0; i < N; i++) shapes[i] = i; // all unique -> worst case
|
|
long start = System.nanoTime();
|
|
buildShapeListLinear(shapes);
|
|
return System.nanoTime() - start;
|
|
}
|
|
|
|
static long benchmarkFixed(int N) {
|
|
int[] shapes = new int[N];
|
|
for (int i = 0; i < N; i++) shapes[i] = i;
|
|
long start = System.nanoTime();
|
|
buildShapeSetFixed(shapes);
|
|
return System.nanoTime() - start;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== calligra-0001: KoShapeManager addShape dedup O(N^2) ===");
|
|
|
|
// Correctness: unique shapes
|
|
{
|
|
int[] shapes = {10, 20, 30, 40, 50};
|
|
List<Integer> linear = buildShapeListLinear(shapes);
|
|
Set<Integer> fixed = buildShapeSetFixed(shapes);
|
|
if (!new HashSet<>(linear).equals(fixed)) {
|
|
System.err.println("FAIL: unique-shape mismatch linear=" + linear + " fixed=" + fixed);
|
|
System.exit(1);
|
|
}
|
|
System.out.println("PASS: unique shapes, result size=" + fixed.size());
|
|
}
|
|
|
|
// Correctness: duplicate shapes (setShapes called with duplicates)
|
|
{
|
|
int[] shapes = {1, 2, 3, 2, 1, 4};
|
|
List<Integer> linear = buildShapeListLinear(shapes);
|
|
Set<Integer> fixed = buildShapeSetFixed(shapes);
|
|
if (!new HashSet<>(linear).equals(fixed)) {
|
|
System.err.println("FAIL: duplicate-shape mismatch linear=" + linear + " fixed=" + fixed);
|
|
System.exit(1);
|
|
}
|
|
if (linear.size() != 4 || fixed.size() != 4) {
|
|
System.err.println("FAIL: expected 4 unique shapes, linear=" + linear.size() + " fixed=" + fixed.size());
|
|
System.exit(1);
|
|
}
|
|
System.out.println("PASS: duplicate shapes deduped correctly, result size=" + fixed.size());
|
|
}
|
|
|
|
// Performance test
|
|
int N = 5000;
|
|
// Warmup
|
|
for (int w = 0; w < 3; w++) { benchmarkLinear(N/10); benchmarkFixed(N/10); }
|
|
|
|
long linearNs = benchmarkLinear(N);
|
|
long fixedNs = benchmarkFixed(N);
|
|
double ratio = (double) linearNs / fixedNs;
|
|
|
|
System.out.printf("Linear N=%d: %,d ns%n", N, linearNs);
|
|
System.out.printf("Fixed N=%d: %,d ns%n", N, fixedNs);
|
|
System.out.printf("Speedup: %.1fx%n", ratio);
|
|
|
|
if (ratio < 5.0) {
|
|
System.err.printf("FAIL: expected >= 5x speedup at N=%d, got %.1fx%n", N, ratio);
|
|
System.exit(1);
|
|
}
|
|
System.out.println("PASS: speedup >= 5x");
|
|
System.out.println("PASS: all tests passed");
|
|
}
|
|
}
|