Skip to content

Commit 7073219

Browse files
committed
improvements
1 parent 71e09e5 commit 7073219

17 files changed

Lines changed: 1066 additions & 71 deletions

core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/ParetoJoinMemoPlanner.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,28 +115,32 @@ Result<T> planMemo() {
115115
Result<T> planBeam() {
116116
StatsBuilder stats = new StatsBuilder();
117117
ParetoFrontier<T> beam = new ParetoFrontier<>(beamWidth, tieBreaker);
118+
ParetoFrontier<T> finalBeam = new ParetoFrontier<>(beamWidth, tieBreaker);
118119
for (int i = 0; i < factorCount; i++) {
119120
T seed = seedFactory.apply(i);
120121
if (seed != null) {
121-
add(beam, seed, stats);
122+
add(finalPlan.test(seed) ? finalBeam : beam, seed, stats);
122123
}
123124
}
124125
for (int depth = 1; depth < maxPlanStepCount && !beam.isEmpty(); depth++) {
125126
ParetoFrontier<T> nextBeam = new ParetoFrontier<>(beamWidth, tieBreaker);
126127
beam.forEach((prefix, ignored) -> {
128+
if (finalPlan.test(prefix)) {
129+
finalBeam.add(prefix, costVector.apply(prefix));
130+
return;
131+
}
127132
long candidates = candidateMask.applyAsLong(prefix);
128133
while (candidates != 0L) {
129134
int candidate = Long.numberOfTrailingZeros(candidates);
130135
candidates &= candidates - 1L;
131136
T transition = transitionFactory.apply(prefix, candidate);
132137
if (transition != null) {
133-
add(nextBeam, transition, stats);
138+
add(finalPlan.test(transition) ? finalBeam : nextBeam, transition, stats);
134139
}
135140
}
136141
});
137142
beam = nextBeam;
138143
}
139-
ParetoFrontier<T> finalBeam = new ParetoFrontier<>(beamWidth, tieBreaker);
140144
beam.forEach((plan, cost) -> {
141145
if (finalPlan.test(plan)) {
142146
finalBeam.add(plan, cost);

core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SketchBasedJoinEstimator.java

Lines changed: 136 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,9 @@ private void applyTo(SketchBasedJoinEstimator estimator, State state, byte slot)
12911291
/** True when persisted index metadata changed and index rewrite is pending. */
12921292
private final AtomicBoolean indexDirty = new AtomicBoolean(false);
12931293
private final Map<EstimateCacheKey, EstimateCacheEntry> estimateCache = new ConcurrentHashMap<>();
1294+
private static final PositiveReadinessCache NO_POSITIVE_READINESS_CACHE = new PositiveReadinessCache(
1295+
Long.MIN_VALUE, null, (byte) -1, -1L);
1296+
private volatile PositiveReadinessCache positiveReadinessCache = NO_POSITIVE_READINESS_CACHE;
12941297

12951298
private volatile BooleanSupplier rebuildAllowedSupplier;
12961299
private final Object persistLock = new Object();
@@ -1523,27 +1526,57 @@ public void close() {
15231526

15241527
public boolean isReady() {
15251528
OptimizationScopeState scope = optimizationScope.get();
1526-
if (scope == null) {
1527-
return isReadyUnscoped(null);
1528-
}
15291529
long epoch = rebuildEpoch.get();
15301530
boolean rebuildRequiredNow = rebuildRequired.get();
15311531
boolean sketchesLoadedNow = sketchesLoaded;
1532-
if (scope.readyKnown && scope.readyEpoch == epoch && scope.readyRebuildRequired == rebuildRequiredNow
1532+
if (scope != null && scope.readyKnown && scope.readyEpoch == epoch
1533+
&& scope.readyRebuildRequired == rebuildRequiredNow
15331534
&& scope.readySketchesLoaded == sketchesLoadedNow) {
15341535
return scope.ready;
15351536
}
1537+
State readyState = current;
1538+
byte readySlot = slotByte(slotOf(readyState));
1539+
PositiveReadinessCache cache = positiveReadinessCache;
1540+
if (cache.matches(epoch, rebuildRequiredNow, sketchesLoadedNow, readyState, readySlot,
1541+
slotGenerations[readySlot])) {
1542+
if (scope != null) {
1543+
cacheScopeReadiness(scope, epoch, true);
1544+
}
1545+
return true;
1546+
}
15361547
boolean ready = isReadyUnscoped(scope);
15371548
if (rebuildEpoch.get() == epoch) {
1538-
scope.readyEpoch = epoch;
1539-
scope.readyRebuildRequired = rebuildRequired.get();
1540-
scope.readySketchesLoaded = sketchesLoaded;
1541-
scope.ready = ready;
1542-
scope.readyKnown = true;
1549+
if (scope != null) {
1550+
cacheScopeReadiness(scope, epoch, ready);
1551+
}
1552+
if (ready) {
1553+
rememberPositiveReadiness(epoch);
1554+
}
15431555
}
15441556
return ready;
15451557
}
15461558

1559+
private void cacheScopeReadiness(OptimizationScopeState scope, long epoch, boolean ready) {
1560+
scope.readyEpoch = epoch;
1561+
scope.readyRebuildRequired = rebuildRequired.get();
1562+
scope.readySketchesLoaded = sketchesLoaded;
1563+
scope.ready = ready;
1564+
scope.readyKnown = true;
1565+
}
1566+
1567+
private void rememberPositiveReadiness(long epoch) {
1568+
if ((epoch & 1L) != 0L || rebuildRequired.get() || !sketchesLoaded) {
1569+
return;
1570+
}
1571+
State readyState = current;
1572+
byte readySlot = slotByte(slotOf(readyState));
1573+
positiveReadinessCache = new PositiveReadinessCache(epoch, readyState, readySlot, slotGenerations[readySlot]);
1574+
}
1575+
1576+
private void clearPositiveReadinessCache() {
1577+
positiveReadinessCache = NO_POSITIVE_READINESS_CACHE;
1578+
}
1579+
15471580
int scopedReadinessScansForTesting() {
15481581
OptimizationScopeState scope = optimizationScope.get();
15491582
return scope == null ? 0 : scope.readinessScans;
@@ -1899,6 +1932,7 @@ private void shutdownAsyncIncrementalIngest() {
18991932
public synchronized long rebuild() {
19001933
flushPendingIncremental();
19011934
clearEstimateCacheIfPopulated();
1935+
clearPositiveReadinessCache();
19021936

19031937
boolean rebuildIntoA = !usingA; // remember before toggling
19041938

@@ -2570,13 +2604,12 @@ public JoinEstimate estimate(Component joinVar, String s, String p, String o, St
25702604
synchronized (snap) {
25712605
PatternStats st = statsOf(snap, joinVar, s, p, o, c);
25722606
ArrayOfDoublesSketch bindings = st.sketch == null ? EMPTY : st.sketch;
2573-
return new JoinEstimate(snap, joinVar, bindings, TupleSketchOps.estimatePositiveDistinct(bindings),
2574-
st.card);
2607+
return new JoinEstimate(snap, joinVar, bindings, st.distinct, st.card);
25752608
}
25762609
}
25772610
PatternStats st = patternStats(snap, joinVar, s, p, o, c);
25782611
ArrayOfDoublesSketch bindings = st.sketch == null ? EMPTY : st.sketch;
2579-
return new JoinEstimate(snap, joinVar, bindings, TupleSketchOps.estimatePositiveDistinct(bindings), st.card);
2612+
return new JoinEstimate(snap, joinVar, bindings, st.distinct, st.card);
25802613
}
25812614

25822615
public double estimateCount(Component joinVar, String s, String p, String o, String c) {
@@ -2657,7 +2690,7 @@ private static final class PatternStats {
26572690

26582691
PatternStats(ArrayOfDoublesSketch s, double card) {
26592692
this.sketch = s;
2660-
this.distinct = TupleSketchOps.estimatePositiveDistinct(s);
2693+
this.distinct = TupleSketchOps.estimateDistinct(s);
26612694
this.card = card;
26622695
}
26632696
}
@@ -2985,7 +3018,7 @@ private BindingSketchResult bindingsSketch(State st, Component j, EnumMap<Compon
29853018
if (candidate == null) {
29863019
continue;
29873020
}
2988-
double candidateDistinct = TupleSketchOps.estimatePositiveDistinct(candidate);
3021+
double candidateDistinct = TupleSketchOps.estimateDistinct(candidate);
29893022
if (candidateDistinct < best) {
29903023
best = candidateDistinct;
29913024
candidateForBest = BindingSketchResult.of(candidate, pr);
@@ -3775,7 +3808,7 @@ private Map<String, VarPlanStats> estimateAllUnboundPatternVarStats(StatementPat
37753808
Component component = getComponent(pattern, var);
37763809
ArrayOfDoublesSketch sketch = getSketchForRead(snap,
37773810
new SketchAddress(REC_GLOBAL_COMPONENT, false, (byte) component.ordinal(), (byte) 0, 0, 0));
3778-
double distinct = clampDistinct(TupleSketchOps.estimatePositiveDistinct(sketch), rows);
3811+
double distinct = clampDistinct(TupleSketchOps.estimateDistinct(sketch), rows);
37793812
if (!(Double.isFinite(distinct) && distinct > 0.0d)) {
37803813
distinct = clampDistinct(rows, rows);
37813814
sketch = null;
@@ -3786,6 +3819,21 @@ private Map<String, VarPlanStats> estimateAllUnboundPatternVarStats(StatementPat
37863819
}
37873820

37883821
private TuplePlanEstimate estimateBindingSetAssignment(BindingSetAssignment assignment) {
3822+
OptimizationScopeState scope = optimizationScope.get();
3823+
if (scope != null) {
3824+
BindingSetAssignmentEstimateCacheKey key = new BindingSetAssignmentEstimateCacheKey(assignment);
3825+
Optional<TuplePlanEstimate> cached = scope.bindingSetAssignmentEstimateCache.get(key);
3826+
if (cached != null) {
3827+
return cached.orElse(null);
3828+
}
3829+
TuplePlanEstimate estimate = computeBindingSetAssignmentEstimate(assignment);
3830+
scope.bindingSetAssignmentEstimateCache.put(key, Optional.ofNullable(estimate));
3831+
return estimate;
3832+
}
3833+
return computeBindingSetAssignmentEstimate(assignment);
3834+
}
3835+
3836+
private TuplePlanEstimate computeBindingSetAssignmentEstimate(BindingSetAssignment assignment) {
37893837
Iterable<BindingSet> bindingSets = assignment.getBindingSets();
37903838
double rows = 0.0d;
37913839
Map<String, Set<Value>> valueSets = new HashMap<>();
@@ -4999,9 +5047,34 @@ private static final class OptimizationScopeState {
49995047
private final VariableDictionary variableDictionary = new VariableDictionary();
50005048
private final JoinOrderingSketchIntersectionCache sketchIntersectionCache = new JoinOrderingSketchIntersectionCache();
50015049
private final Map<TuplePlanEstimateCacheKey, Optional<TuplePlanEstimate>> tupleEstimateCache = new HashMap<>();
5050+
private final Map<BindingSetAssignmentEstimateCacheKey, Optional<TuplePlanEstimate>> bindingSetAssignmentEstimateCache = new HashMap<>();
50025051
private final Map<AccessShapeCacheKey, AccessShape> accessShapeCache = new HashMap<>();
50035052
}
50045053

5054+
private static final class PositiveReadinessCache {
5055+
private final long epoch;
5056+
private final State state;
5057+
private final byte slot;
5058+
private final long slotGeneration;
5059+
5060+
private PositiveReadinessCache(long epoch, State state, byte slot, long slotGeneration) {
5061+
this.epoch = epoch;
5062+
this.state = state;
5063+
this.slot = slot;
5064+
this.slotGeneration = slotGeneration;
5065+
}
5066+
5067+
private boolean matches(long currentEpoch, boolean rebuildRequired, boolean sketchesLoaded, State currentState,
5068+
byte currentSlot, long currentSlotGeneration) {
5069+
return !rebuildRequired
5070+
&& sketchesLoaded
5071+
&& epoch == currentEpoch
5072+
&& state == currentState
5073+
&& slot == currentSlot
5074+
&& slotGeneration == currentSlotGeneration;
5075+
}
5076+
}
5077+
50055078
private static final class TuplePlanEstimateCacheKey {
50065079
private final TupleExpr tupleExpr;
50075080
private final long boundVarMask;
@@ -5031,6 +5104,35 @@ public int hashCode() {
50315104
}
50325105
}
50335106

5107+
private static final class BindingSetAssignmentEstimateCacheKey {
5108+
private final Iterable<BindingSet> bindingSets;
5109+
private final Set<String> bindingNames;
5110+
private final int hashCode;
5111+
5112+
private BindingSetAssignmentEstimateCacheKey(BindingSetAssignment assignment) {
5113+
this.bindingSets = assignment.getBindingSets();
5114+
this.bindingNames = Set.copyOf(assignment.getBindingNames());
5115+
this.hashCode = 31 * System.identityHashCode(bindingSets) + bindingNames.hashCode();
5116+
}
5117+
5118+
@Override
5119+
public boolean equals(Object other) {
5120+
if (this == other) {
5121+
return true;
5122+
}
5123+
if (!(other instanceof BindingSetAssignmentEstimateCacheKey)) {
5124+
return false;
5125+
}
5126+
BindingSetAssignmentEstimateCacheKey that = (BindingSetAssignmentEstimateCacheKey) other;
5127+
return bindingSets == that.bindingSets && bindingNames.equals(that.bindingNames);
5128+
}
5129+
5130+
@Override
5131+
public int hashCode() {
5132+
return hashCode;
5133+
}
5134+
}
5135+
50345136
private static final class AccessShapeCacheKey {
50355137
private final TupleExpr tupleExpr;
50365138
private final long boundVarMask;
@@ -5328,16 +5430,30 @@ private TuplePlanEstimate estimateTupleExprPlan(TupleExpr tupleExpr, long initia
53285430

53295431
private TuplePlanEstimate estimateTupleExprPlan(TupleExpr tupleExpr, OptimizationScopeState scope,
53305432
long initiallyBoundVarMask) {
5331-
TuplePlanEstimateCacheKey key = new TuplePlanEstimateCacheKey(tupleExpr, initiallyBoundVarMask);
5433+
long cacheBoundVarMask = canonicalTupleEstimateBoundMask(tupleExpr, scope, initiallyBoundVarMask);
5434+
TuplePlanEstimateCacheKey key = new TuplePlanEstimateCacheKey(tupleExpr, cacheBoundVarMask);
53325435
Optional<TuplePlanEstimate> cached = scope.tupleEstimateCache.get(key);
53335436
if (cached != null) {
53345437
return cached.orElse(null);
53355438
}
5336-
TuplePlanEstimate estimate = computeTupleExprPlan(tupleExpr, scope, initiallyBoundVarMask);
5439+
TuplePlanEstimate estimate = computeTupleExprPlan(tupleExpr, scope, cacheBoundVarMask);
53375440
scope.tupleEstimateCache.put(key, Optional.ofNullable(estimate));
53385441
return estimate;
53395442
}
53405443

5444+
private long canonicalTupleEstimateBoundMask(TupleExpr tupleExpr, OptimizationScopeState scope,
5445+
long initiallyBoundVarMask) {
5446+
if (initiallyBoundVarMask == 0L || initiallyBoundVarMask == BOUND_VAR_MASK_OVERFLOW
5447+
|| !(tupleExpr instanceof BindingSetAssignment)) {
5448+
return initiallyBoundVarMask;
5449+
}
5450+
long assignmentMask = scope.variableDictionary.maskOf(((BindingSetAssignment) tupleExpr).getBindingNames());
5451+
if (assignmentMask == BOUND_VAR_MASK_OVERFLOW) {
5452+
return initiallyBoundVarMask;
5453+
}
5454+
return initiallyBoundVarMask & assignmentMask;
5455+
}
5456+
53415457
private TuplePlanEstimate computeTupleExprPlan(TupleExpr tupleExpr, Set<String> initiallyBoundVars) {
53425458
if (tupleExpr == null) {
53435459
return null;
@@ -7912,6 +8028,7 @@ public void discardAndMarkForRebuild() {
79128028

79138029
private void unloadInternal(boolean markRebuildRequired) {
79148030
clearEstimateCache();
8031+
clearPositiveReadinessCache();
79158032
if (persistenceEnabled && persistenceStore != null) {
79168033
if ((dirty.get() || indexDirty.get()) && !persistIfDirty()) {
79178034
return;
@@ -8001,6 +8118,7 @@ private void loadDirectoryStore(boolean markLoaded) throws IOException {
80018118
if (store == null || !store.hasMetadata()) {
80028119
return;
80038120
}
8121+
clearPositiveReadinessCache();
80048122
SketchEstimatorMetadata metadata = store.readMetadata();
80058123
if (metadata.subjectBucketCount != subjectBucketCount || metadata.predicateBucketCount != predicateBucketCount
80068124
|| metadata.objectBucketCount != objectBucketCount
@@ -8101,6 +8219,7 @@ private void clearPersistedSketchDirectory() {
81018219

81028220
indexDirty.set(false);
81038221
}
8222+
clearPositiveReadinessCache();
81048223
}
81058224

81068225
private boolean isRebuildAllowed() {

core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SketchJoinOrderPlanner.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Arrays;
1919
import java.util.HashMap;
2020
import java.util.HashSet;
21+
import java.util.IdentityHashMap;
2122
import java.util.Iterator;
2223
import java.util.LinkedHashSet;
2324
import java.util.List;
@@ -138,6 +139,7 @@ final class SketchJoinOrderPlanner {
138139
private final Map<LocalFilterPassRatioKey, Double> localFilterPassRatioMemo = new HashMap<>();
139140
private final Map<Long, Double> finiteBindingAssignmentPrefixRowsMemo = new HashMap<>();
140141
private final Map<FiniteBindingVariableValuesKey, Set<Value>> finiteBindingVariableValuesMemo = new HashMap<>();
142+
private final Map<ValueExpr, Map<Long, Double>> finiteDomainConditionPassRatioMemo = new IdentityHashMap<>();
141143
private final SketchBasedJoinEstimator.SketchPlannerPath factorRejectionPath;
142144
private final TupleExpr rejectedFactor;
143145
private final List<JoinOrderPlanner.FilterConstraint> deferredFilters;
@@ -1349,6 +1351,18 @@ private double finiteDomainFilterPassRatio(JoinOrderPlanner.FilterConstraint fil
13491351
}
13501352

13511353
private double finiteDomainConditionPassRatio(ValueExpr condition, long factorMask) {
1354+
Map<Long, Double> ratiosByMask = finiteDomainConditionPassRatioMemo.computeIfAbsent(condition,
1355+
ignored -> new HashMap<>());
1356+
Double ratio = ratiosByMask.get(factorMask);
1357+
if (ratio != null) {
1358+
return ratio.doubleValue();
1359+
}
1360+
ratio = computeFiniteDomainConditionPassRatio(condition, factorMask);
1361+
ratiosByMask.put(factorMask, ratio);
1362+
return ratio.doubleValue();
1363+
}
1364+
1365+
private double computeFiniteDomainConditionPassRatio(ValueExpr condition, long factorMask) {
13521366
double enumeratedRatio = finiteDomainConditionPassRatioByEnumeration(condition, factorMask);
13531367
if (isValidPassRatio(enumeratedRatio)) {
13541368
return enumeratedRatio;

core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/TupleSketchOps.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ static double estimatePositiveDistinct(ArrayOfDoublesSketch sketch) {
110110
return scaleByTheta(count, sketch);
111111
}
112112

113+
static double estimateDistinct(ArrayOfDoublesSketch sketch) {
114+
if (sketch == null || sketch.getRetainedEntries() == 0) {
115+
return 0.0d;
116+
}
117+
return sketch.getEstimate();
118+
}
119+
113120
static double estimateIntersectionProductSum(ArrayOfDoublesSketch left, ArrayOfDoublesSketch right, int k) {
114121
return intersectProductStats(left, right, k).positiveSum();
115122
}

0 commit comments

Comments
 (0)