wave10 complete: 472/219
This commit is contained in:
parent
70702dff5c
commit
6276aea2e5
28 changed files with 929 additions and 5 deletions
278
defects/lmdb/unit/LmdbDbiOpenAlgorithm.java
Normal file
278
defects/lmdb/unit/LmdbDbiOpenAlgorithm.java
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Standalone unit test for lmdb-001:
|
||||
* mdb_dbi_open() named-database linear scan — O(D) per open.
|
||||
*
|
||||
* Models the exact C algorithm from libraries/liblmdb/mdb.c lines 10932-10943.
|
||||
* Compile: javac -d . *.java
|
||||
* Run: java unit.LmdbDbiOpenAlgorithm
|
||||
*/
|
||||
public class LmdbDbiOpenAlgorithm {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Model types
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class DbEntry {
|
||||
final String name;
|
||||
final int dbi;
|
||||
DbEntry(String name, int dbi) { this.name = name; this.dbi = dbi; }
|
||||
}
|
||||
|
||||
static class Result {
|
||||
final int dbi; // -1 = not found
|
||||
final int comparisons;
|
||||
Result(int dbi, int cmp) { this.dbi = dbi; this.comparisons = cmp; }
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Defective algorithm (exact port from LMDB mdb_dbi_open, mdb.c:10932-10943)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class DefectiveDbiOpen {
|
||||
// dbxs: array of open named databases (index = dbi)
|
||||
// name: name to look up
|
||||
static Result lookup(List<DbEntry> dbxs, String name) {
|
||||
int comparisons = 0;
|
||||
int len = name.length();
|
||||
|
||||
for (int i = 0; i < dbxs.size(); i++) {
|
||||
DbEntry entry = dbxs.get(i);
|
||||
if (entry == null || entry.name == null || entry.name.isEmpty()) {
|
||||
// free slot — skip
|
||||
comparisons++;
|
||||
continue;
|
||||
}
|
||||
comparisons++;
|
||||
if (len == entry.name.length() && name.equals(entry.name)) {
|
||||
return new Result(entry.dbi, comparisons);
|
||||
}
|
||||
}
|
||||
return new Result(-1, comparisons); // not found
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Fixed algorithm: O(log D) binary search on sorted name array
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static class FixedDbiOpen {
|
||||
// Maintains a sorted index of (name, dbi) pairs
|
||||
private final TreeMap<String, Integer> index = new TreeMap<>();
|
||||
int comparisons = 0;
|
||||
|
||||
void register(String name, int dbi) {
|
||||
index.put(name, dbi);
|
||||
}
|
||||
|
||||
Result lookup(String name) {
|
||||
comparisons = 0;
|
||||
// TreeMap.get is O(log D)
|
||||
// We simulate comparison count as ceil(log2(size)) for binary search
|
||||
int size = index.size();
|
||||
int steps = size == 0 ? 0 : (int) Math.ceil(Math.log(size + 1) / Math.log(2));
|
||||
comparisons = Math.max(1, steps);
|
||||
Integer dbi = index.get(name);
|
||||
return new Result(dbi == null ? -1 : dbi, comparisons);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static int passed = 0, failed = 0;
|
||||
|
||||
static void expect(String label, boolean condition) {
|
||||
if (condition) {
|
||||
System.out.println(" PASS: " + label);
|
||||
passed++;
|
||||
} else {
|
||||
System.out.println(" FAIL: " + label);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
static List<DbEntry> buildDbxs(int d) {
|
||||
List<DbEntry> dbxs = new ArrayList<>();
|
||||
for (int i = 0; i < d; i++) {
|
||||
dbxs.add(new DbEntry("db_" + String.format("%04d", i), i));
|
||||
}
|
||||
return dbxs;
|
||||
}
|
||||
|
||||
static FixedDbiOpen buildFixed(int d) {
|
||||
FixedDbiOpen f = new FixedDbiOpen();
|
||||
for (int i = 0; i < d; i++) {
|
||||
f.register("db_" + String.format("%04d", i), i);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
static void testBasicLookup() {
|
||||
System.out.println("[testBasicLookup]");
|
||||
List<DbEntry> dbxs = buildDbxs(10);
|
||||
FixedDbiOpen fixed = buildFixed(10);
|
||||
|
||||
Result slow = DefectiveDbiOpen.lookup(dbxs, "db_0005");
|
||||
Result fast = fixed.lookup("db_0005");
|
||||
|
||||
expect("defective finds dbi=5", slow.dbi == 5);
|
||||
expect("fixed finds dbi=5", fast.dbi == 5);
|
||||
}
|
||||
|
||||
static void testNotFound() {
|
||||
System.out.println("[testNotFound]");
|
||||
List<DbEntry> dbxs = buildDbxs(10);
|
||||
FixedDbiOpen fixed = buildFixed(10);
|
||||
|
||||
Result slow = DefectiveDbiOpen.lookup(dbxs, "no_such_db");
|
||||
Result fast = fixed.lookup("no_such_db");
|
||||
|
||||
expect("defective returns -1 (not found)", slow.dbi == -1);
|
||||
expect("fixed returns -1 (not found)", fast.dbi == -1);
|
||||
expect("defective scanned all D=10 entries", slow.comparisons == 10);
|
||||
}
|
||||
|
||||
static void testFirstEntry() {
|
||||
System.out.println("[testFirstEntry]");
|
||||
List<DbEntry> dbxs = buildDbxs(50);
|
||||
FixedDbiOpen fixed = buildFixed(50);
|
||||
|
||||
Result slow = DefectiveDbiOpen.lookup(dbxs, "db_0000");
|
||||
Result fast = fixed.lookup("db_0000");
|
||||
|
||||
expect("defective finds first entry", slow.dbi == 0);
|
||||
expect("fixed finds first entry", fast.dbi == 0);
|
||||
// Defective finds it at position 0 — O(1) best case, O(D) worst case
|
||||
}
|
||||
|
||||
static void testLinearVsLogScaling_D100() {
|
||||
System.out.println("[testLinearVsLogScaling D=100]");
|
||||
int D = 100;
|
||||
int N = 10000; // lookup operations
|
||||
|
||||
List<DbEntry> dbxs = buildDbxs(D);
|
||||
FixedDbiOpen fixed = buildFixed(D);
|
||||
|
||||
// Worst case: look up last entry every time
|
||||
String target = "db_" + String.format("%04d", D - 1);
|
||||
long slowTotal = 0;
|
||||
long fastTotal = 0;
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
slowTotal += DefectiveDbiOpen.lookup(dbxs, target).comparisons;
|
||||
fastTotal += fixed.lookup(target).comparisons;
|
||||
}
|
||||
|
||||
System.out.println(" defective total comparisons: " + slowTotal);
|
||||
System.out.println(" fixed total comparisons: " + fastTotal);
|
||||
System.out.println(" expected slowTotal ~ N*D = " + (long)N * D);
|
||||
|
||||
// Defective: worst case = D comparisons per lookup → N*D total
|
||||
expect("defective is O(D) per lookup (total ~ N*D)",
|
||||
slowTotal >= (long)(N * D * 9 / 10));
|
||||
|
||||
// Fixed: O(log D) per lookup → N*log2(D) total
|
||||
double expectedFast = N * Math.ceil(Math.log(D + 1) / Math.log(2));
|
||||
expect("fixed is O(log D) per lookup",
|
||||
fastTotal <= (long)(expectedFast * 2));
|
||||
|
||||
double ratio = (double) slowTotal / fastTotal;
|
||||
System.out.printf(" speedup ratio: %.1fx%n", ratio);
|
||||
expect("speedup >= 5x at D=100", ratio >= 5.0);
|
||||
}
|
||||
|
||||
static void testLinearVsLogScaling_D1000() {
|
||||
System.out.println("[testLinearVsLogScaling D=1000]");
|
||||
int D = 1000;
|
||||
int N = 1000;
|
||||
|
||||
List<DbEntry> dbxs = buildDbxs(D);
|
||||
FixedDbiOpen fixed = buildFixed(D);
|
||||
|
||||
String target = "db_" + String.format("%04d", D - 1);
|
||||
long slowTotal = 0;
|
||||
long fastTotal = 0;
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
slowTotal += DefectiveDbiOpen.lookup(dbxs, target).comparisons;
|
||||
fastTotal += fixed.lookup(target).comparisons;
|
||||
}
|
||||
|
||||
System.out.println(" defective total comparisons: " + slowTotal);
|
||||
System.out.println(" fixed total comparisons: " + fastTotal);
|
||||
|
||||
expect("defective is O(D) per lookup",
|
||||
slowTotal >= (long)(N * D * 9 / 10));
|
||||
|
||||
double ratio = (double) slowTotal / fastTotal;
|
||||
System.out.printf(" speedup ratio: %.1fx%n", ratio);
|
||||
expect("speedup >= 50x at D=1000", ratio >= 50.0);
|
||||
}
|
||||
|
||||
static void testMultipleOpensPerTransaction() {
|
||||
System.out.println("[testMultipleOpensPerTransaction]");
|
||||
int D = 50; // 50 named databases
|
||||
int K = 10; // databases opened per transaction
|
||||
int N = 500; // transactions
|
||||
|
||||
List<DbEntry> dbxs = buildDbxs(D);
|
||||
FixedDbiOpen fixed = buildFixed(D);
|
||||
|
||||
long slowTotal = 0;
|
||||
long fastTotal = 0;
|
||||
|
||||
Random rng = new Random(123);
|
||||
for (int txn = 0; txn < N; txn++) {
|
||||
for (int k = 0; k < K; k++) {
|
||||
String name = "db_" + String.format("%04d", rng.nextInt(D));
|
||||
slowTotal += DefectiveDbiOpen.lookup(dbxs, name).comparisons;
|
||||
fastTotal += fixed.lookup(name).comparisons;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(" defective total comparisons over " + N + " txns: " + slowTotal);
|
||||
System.out.println(" fixed total comparisons over " + N + " txns: " + fastTotal);
|
||||
|
||||
expect("defective uses more comparisons", slowTotal > fastTotal);
|
||||
|
||||
double ratio = (double) slowTotal / fastTotal;
|
||||
System.out.printf(" speedup ratio: %.1fx%n", ratio);
|
||||
expect("speedup >= 3x", ratio >= 3.0);
|
||||
}
|
||||
|
||||
static void testEmptyDatabase() {
|
||||
System.out.println("[testEmptyDatabase]");
|
||||
List<DbEntry> dbxs = new ArrayList<>();
|
||||
FixedDbiOpen fixed = new FixedDbiOpen();
|
||||
|
||||
Result slow = DefectiveDbiOpen.lookup(dbxs, "anydb");
|
||||
Result fast = fixed.lookup("anydb");
|
||||
|
||||
expect("defective: not found in empty", slow.dbi == -1);
|
||||
expect("fixed: not found in empty", fast.dbi == -1);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Main
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== lmdb-001: mdb_dbi_open named-database linear scan ===");
|
||||
testBasicLookup();
|
||||
testNotFound();
|
||||
testFirstEntry();
|
||||
testEmptyDatabase();
|
||||
testLinearVsLogScaling_D100();
|
||||
testLinearVsLogScaling_D1000();
|
||||
testMultipleOpensPerTransaction();
|
||||
|
||||
System.out.println();
|
||||
System.out.println("Results: " + passed + " passed, " + failed + " failed");
|
||||
if (failed > 0) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue