220 lines
9.3 KiB
Java
220 lines
9.3 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* sqlalchemy-0003: evaluated_keys list → set in _apply_evaluators()
|
||
*
|
||
* Models SQLAlchemy bulk_persistence.py _apply_evaluators():
|
||
* evaluated_keys = list(value_evaluators.keys())
|
||
* then: c.key not in evaluated_keys (inside set comprehension over prefetch_cols)
|
||
* and: .difference(evaluated_keys) (set subtraction)
|
||
*
|
||
* Fix: evaluated_keys = set(value_evaluators)
|
||
*
|
||
* Standalone Java — no JUnit required.
|
||
*/
|
||
public class EvaluatedKeysAlgorithm {
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Simulated column model
|
||
// -----------------------------------------------------------------------
|
||
static class Column {
|
||
final String key;
|
||
Column(String key) { this.key = key; }
|
||
@Override public boolean equals(Object o) {
|
||
return o instanceof Column && ((Column) o).key.equals(this.key);
|
||
}
|
||
@Override public int hashCode() { return key.hashCode(); }
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// SLOW: evaluated_keys as plain List
|
||
// -----------------------------------------------------------------------
|
||
static Result slowApplyEvaluators(Map<String, Object> valueEvaluators,
|
||
List<Column> prefetchCols,
|
||
List<Column> postfetchCols) {
|
||
List<String> evaluatedKeys = new ArrayList<>(valueEvaluators.keySet()); // ← plain list
|
||
|
||
// to_prefetch: {c for c in prefetch_cols if c.key not in evaluated_keys}
|
||
Set<Column> toPrefetch = new HashSet<>();
|
||
for (Column c : prefetchCols) {
|
||
if (!evaluatedKeys.contains(c.key)) { // ← O(K) scan
|
||
toPrefetch.add(c);
|
||
}
|
||
}
|
||
|
||
// to_expire = {col.key for col in postfetch_cols}.difference(evaluated_keys)
|
||
Set<String> toExpire = new HashSet<>();
|
||
for (Column c : postfetchCols) {
|
||
toExpire.add(c.key);
|
||
}
|
||
toExpire.removeAll(evaluatedKeys); // removeAll(List) = O(P×K)
|
||
|
||
return new Result(toPrefetch, toExpire);
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// FAST: evaluated_keys as HashSet
|
||
// -----------------------------------------------------------------------
|
||
static Result fastApplyEvaluators(Map<String, Object> valueEvaluators,
|
||
List<Column> prefetchCols,
|
||
List<Column> postfetchCols) {
|
||
Set<String> evaluatedKeys = new HashSet<>(valueEvaluators.keySet()); // ← set (or just use valueEvaluators directly)
|
||
|
||
Set<Column> toPrefetch = new HashSet<>();
|
||
for (Column c : prefetchCols) {
|
||
if (!evaluatedKeys.contains(c.key)) { // ← O(1)
|
||
toPrefetch.add(c);
|
||
}
|
||
}
|
||
|
||
Set<String> toExpire = new HashSet<>();
|
||
for (Column c : postfetchCols) {
|
||
toExpire.add(c.key);
|
||
}
|
||
toExpire.removeAll(evaluatedKeys); // removeAll(Set) = O(P)
|
||
|
||
return new Result(toPrefetch, toExpire);
|
||
}
|
||
|
||
static class Result {
|
||
final Set<Column> toPrefetch;
|
||
final Set<String> toExpire;
|
||
Result(Set<Column> toPrefetch, Set<String> toExpire) {
|
||
this.toPrefetch = toPrefetch;
|
||
this.toExpire = toExpire;
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Test helpers
|
||
// -----------------------------------------------------------------------
|
||
static Map<String, Object> makeEvaluators(int n) {
|
||
Map<String, Object> m = new LinkedHashMap<>();
|
||
for (int i = 0; i < n; i++) {
|
||
m.put("eval_key_" + i, new Object());
|
||
}
|
||
return m;
|
||
}
|
||
|
||
static List<Column> makeCols(int n, String prefix) {
|
||
List<Column> cols = new ArrayList<>();
|
||
for (int i = 0; i < n; i++) {
|
||
cols.add(new Column(prefix + i));
|
||
}
|
||
return cols;
|
||
}
|
||
|
||
// -----------------------------------------------------------------------
|
||
// Main
|
||
// -----------------------------------------------------------------------
|
||
public static void main(String[] args) {
|
||
int passed = 0;
|
||
int total = 0;
|
||
|
||
// ---- Correctness: basic case ----
|
||
{
|
||
Map<String, Object> evals = new LinkedHashMap<>();
|
||
evals.put("col_a", new Object());
|
||
evals.put("col_b", new Object());
|
||
|
||
List<Column> prefetch = Arrays.asList(
|
||
new Column("col_a"), // in evaluatedKeys → NOT in toPrefetch
|
||
new Column("col_c"), // not in evaluatedKeys → in toPrefetch
|
||
new Column("col_d") // not in evaluatedKeys → in toPrefetch
|
||
);
|
||
List<Column> postfetch = Arrays.asList(
|
||
new Column("col_a"), // in evaluatedKeys → NOT in toExpire
|
||
new Column("col_b"), // in evaluatedKeys → NOT in toExpire
|
||
new Column("col_e") // not in evaluatedKeys → in toExpire
|
||
);
|
||
|
||
Result slow = slowApplyEvaluators(evals, prefetch, postfetch);
|
||
Result fast = fastApplyEvaluators(evals, prefetch, postfetch);
|
||
|
||
total++;
|
||
assert slow.toPrefetch.size() == fast.toPrefetch.size()
|
||
: "toPrefetch size: slow=" + slow.toPrefetch.size() + " fast=" + fast.toPrefetch.size();
|
||
assert slow.toExpire.size() == fast.toExpire.size()
|
||
: "toExpire size: slow=" + slow.toExpire.size() + " fast=" + fast.toExpire.size();
|
||
assert slow.toPrefetch.size() == 2 : "expected 2 in toPrefetch, got " + slow.toPrefetch.size();
|
||
assert slow.toExpire.size() == 1 : "expected 1 in toExpire, got " + slow.toExpire.size();
|
||
System.out.println("PASS 1/4: correctness basic case");
|
||
passed++;
|
||
}
|
||
|
||
// ---- Correctness: empty evaluators ----
|
||
{
|
||
Map<String, Object> evals = new HashMap<>();
|
||
List<Column> prefetch = Arrays.asList(new Column("c1"), new Column("c2"));
|
||
List<Column> postfetch = Arrays.asList(new Column("c3"));
|
||
|
||
Result slow = slowApplyEvaluators(evals, prefetch, postfetch);
|
||
Result fast = fastApplyEvaluators(evals, prefetch, postfetch);
|
||
|
||
total++;
|
||
assert slow.toPrefetch.size() == fast.toPrefetch.size() : "empty evals toPrefetch mismatch";
|
||
assert slow.toExpire.size() == fast.toExpire.size() : "empty evals toExpire mismatch";
|
||
// When no evaluators, all cols go to prefetch, all to expire
|
||
assert slow.toPrefetch.size() == 2 : "expected 2 prefetch";
|
||
assert slow.toExpire.size() == 1 : "expected 1 expire";
|
||
System.out.println("PASS 2/4: correctness empty evaluators");
|
||
passed++;
|
||
}
|
||
|
||
// ---- Performance: N=1000 evaluated keys, 2000 cols ----
|
||
{
|
||
int K = 1000;
|
||
int C = 2000;
|
||
Map<String, Object> evals = makeEvaluators(K);
|
||
// half prefetch cols are in evaluated keys
|
||
List<Column> prefetch = new ArrayList<>();
|
||
for (int i = 0; i < C/2; i++) prefetch.add(new Column("eval_key_" + i));
|
||
for (int i = 0; i < C/2; i++) prefetch.add(new Column("other_" + i));
|
||
|
||
List<Column> postfetch = new ArrayList<>();
|
||
for (int i = 0; i < C/2; i++) postfetch.add(new Column("eval_key_" + i));
|
||
for (int i = 0; i < C/2; i++) postfetch.add(new Column("post_" + i));
|
||
|
||
long t0 = System.nanoTime();
|
||
for (int iter = 0; iter < 10; iter++) slowApplyEvaluators(evals, prefetch, postfetch);
|
||
long slowNs = (System.nanoTime() - t0) / 10;
|
||
|
||
long t1 = System.nanoTime();
|
||
for (int iter = 0; iter < 10; iter++) fastApplyEvaluators(evals, prefetch, postfetch);
|
||
long fastNs = (System.nanoTime() - t1) / 10;
|
||
|
||
double ratio = (double) slowNs / fastNs;
|
||
total++;
|
||
System.out.printf("PERF 3/4: sqlalchemy-0003 K=%d C=%d slow=%dms fast=%dms ratio=%.1fx%n",
|
||
K, C, slowNs/1_000_000, fastNs/1_000_000, ratio);
|
||
assert ratio >= 5.0 : "ratio too low: " + ratio;
|
||
System.out.println("PASS 3/4: sqlalchemy-0003 ratio >= 5x");
|
||
passed++;
|
||
}
|
||
|
||
// ---- Edge case: all cols in evaluated_keys ----
|
||
{
|
||
Map<String, Object> evals = makeEvaluators(50);
|
||
List<Column> prefetch = new ArrayList<>();
|
||
List<Column> postfetch = new ArrayList<>();
|
||
for (int i = 0; i < 50; i++) {
|
||
prefetch.add(new Column("eval_key_" + i));
|
||
postfetch.add(new Column("eval_key_" + i));
|
||
}
|
||
|
||
Result slow = slowApplyEvaluators(evals, prefetch, postfetch);
|
||
Result fast = fastApplyEvaluators(evals, prefetch, postfetch);
|
||
|
||
total++;
|
||
assert slow.toPrefetch.isEmpty() && fast.toPrefetch.isEmpty() : "all-in-evals toPrefetch not empty";
|
||
assert slow.toExpire.isEmpty() && fast.toExpire.isEmpty() : "all-in-evals toExpire not empty";
|
||
System.out.println("PASS 4/4: edge case all cols in evaluated_keys");
|
||
passed++;
|
||
}
|
||
|
||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||
if (passed < total) throw new AssertionError("Some tests failed");
|
||
}
|
||
}
|