Skip to content

Commit 2acb7ca

Browse files
committed
lmdb store gets its own optimizer pipeline and custom optimizers
1 parent cc889b0 commit 2acb7ca

15 files changed

Lines changed: 1854 additions & 14 deletions

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,6 @@ enum SketchPlannerPath {
451451
private static final long MIN_ROBUST_REBUILD_HEADROOM_BYTES = 256L * 1024L * 1024L;
452452
private static final long ROBUST_REBUILD_DISABLE_HEADROOM_PERCENT = 2L;
453453
private static final long ROBUST_REBUILD_ENABLE_HEADROOM_PERCENT = 10L;
454-
private static final long ROBUST_REBUILD_FAILURE_BACKOFF_MILLIS = 1000L;
455-
private static final long ROBUST_HEADROOM_GC_INTERVAL_MILLIS = 5000L;
456-
private static final long ROBUST_HEADROOM_CHECK_INTERVAL = 4096L;
457454
private static final byte[] FILE_GROWTH_MARKER = new byte[] { 0 };
458455
private static final String MIN_SKETCH_MEMORY_PERCENT_PROPERTY = "minSketchMemoryPercent";
459456
private static final String MAX_SKETCH_MEMORY_PERCENT_PROPERTY = "maxSketchMemoryPercent";

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

Lines changed: 246 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ final class SketchJoinOrderPlanner {
4949
private static final int FRONTIER_LIMIT = 4;
5050
private static final int GREEDY_BEAM_WIDTH = 4;
5151
private static final double STRUCTURAL_WORK_EQUIVALENCE_ROWS = 4.0d;
52+
private static final double BRIDGE_UNLOCK_MAX_STEP_WORK_RATIO = 2.0d;
53+
private static final int COMPLETED_DEFERRED_FILTER_CONNECTIONS = 2;
54+
private static final double DEFERRED_FILTER_ORDERING_MAX_PASS_RATIO = 0.5d;
5255
private static final String TRACE_DIAGNOSTICS_PROPERTY = "rdf4j.optimizer.sketchPlanner.traceDiagnostics";
5356
static final double SMALL_BINDING_SET_ASSIGNMENT_MAX_ROWS = 64.0d;
5457
private static final Logger logger = LoggerFactory.getLogger(SketchJoinOrderPlanner.class);
@@ -65,6 +68,7 @@ final class SketchJoinOrderPlanner {
6568
private final long[] joinVarMasks;
6669
private final long[] connectivityVarMasks;
6770
private final long[] bindingVarMasks;
71+
private final long[] runtimeVarMasks;
6872
private final long[] deferredFilterRequiredVarMasks;
6973
private final int stateMemoSize;
7074
private final long[] variablesMaskByState;
@@ -146,6 +150,7 @@ private SketchJoinOrderPlanner(SketchBasedJoinEstimator estimator,
146150
this.joinVarMasks = buildFactorMasks(this.factors, variableIds, PlanFactorMaskKind.JOIN);
147151
this.connectivityVarMasks = buildFactorMasks(this.factors, variableIds, PlanFactorMaskKind.CONNECTIVITY);
148152
this.bindingVarMasks = buildFactorMasks(this.factors, variableIds, PlanFactorMaskKind.BINDING);
153+
this.runtimeVarMasks = buildRuntimeVarMasks(this.factors, variableIds);
149154
this.deferredFilterRequiredVarMasks = buildDeferredFilterRequiredVarMasks(this.deferredFilters, variableIds);
150155
this.stateMemoSize = stateMemoSize(this.factors.size());
151156
this.variablesMaskByState = new long[stateMemoSize];
@@ -257,7 +262,7 @@ private FactorBuildResult buildFactors(List<TupleExpr> expressions) {
257262
if (estimate == null) {
258263
return new FactorBuildResult(List.of(), unsupportedPath(tupleExpr), tupleExpr);
259264
}
260-
Set<String> bindingVars = plannerVariables(tupleExpr.getBindingNames());
265+
Set<String> bindingVars = factorBindingVars(tupleExpr);
261266
providedVars.addAll(bindingVars);
262267
pendingFactors.add(new PendingPlanFactor(i, tupleExpr, estimate, bindingVars));
263268
}
@@ -266,11 +271,21 @@ private FactorBuildResult buildFactors(List<TupleExpr> expressions) {
266271
Set<String> connectivityVars = connectivityVars(pending.tupleExpr(), pending.joinVars(),
267272
providedVars);
268273
builtFactors.add(new PlanFactor(pending.index(), pending.tupleExpr(), pending.estimate(),
269-
pending.joinVars(), connectivityVars, Set.copyOf(pending.tupleExpr().getBindingNames())));
274+
pending.joinVars(), connectivityVars, pending.joinVars()));
270275
}
271276
return new FactorBuildResult(List.copyOf(builtFactors), null, null);
272277
}
273278

279+
private static Set<String> factorBindingVars(TupleExpr tupleExpr) {
280+
if (tupleExpr instanceof BindingSetAssignment) {
281+
return plannerVariables(tupleExpr.getBindingNames());
282+
}
283+
if (tupleExpr instanceof Filter filter) {
284+
return factorBindingVars(filter.getArg());
285+
}
286+
return plannerVariables(VarNameCollector.process(tupleExpr));
287+
}
288+
274289
private static Set<String> plannerVariables(Set<String> variables) {
275290
if (variables == null || variables.isEmpty()) {
276291
return Set.of();
@@ -357,6 +372,14 @@ private static long[] buildFactorMasks(List<PlanFactor> factors, Map<String, Int
357372
return masks;
358373
}
359374

375+
private static long[] buildRuntimeVarMasks(List<PlanFactor> factors, Map<String, Integer> variableIds) {
376+
long[] masks = new long[factors.size()];
377+
for (int i = 0; i < factors.size(); i++) {
378+
masks[i] = variableMask(factorBindingVars(factors.get(i).tupleExpr()), variableIds);
379+
}
380+
return masks;
381+
}
382+
360383
private static long[] buildDeferredFilterRequiredVarMasks(List<JoinOrderPlanner.FilterConstraint> deferredFilters,
361384
Map<String, Integer> variableIds) {
362385
long[] masks = new long[deferredFilters.size()];
@@ -1224,6 +1247,118 @@ private double newlyUnlockedFilterPassRatio(long previousMask, long nextMask) {
12241247
return found ? passRatio : 1.0d;
12251248
}
12261249

1250+
private int compareBoundGuardOrder(List<Integer> left, List<Integer> right) {
1251+
int size = Math.min(left.size(), right.size());
1252+
long leftMask = 0L;
1253+
long rightMask = 0L;
1254+
for (int i = 0; i < size; i++) {
1255+
int leftIndex = left.get(i);
1256+
int rightIndex = right.get(i);
1257+
if (leftIndex != rightIndex) {
1258+
boolean leftGuard = isBoundGuardFactor(leftIndex, leftMask);
1259+
boolean rightGuard = isBoundGuardFactor(rightIndex, rightMask);
1260+
if (leftGuard != rightGuard) {
1261+
return leftGuard ? -1 : 1;
1262+
}
1263+
}
1264+
leftMask |= bit(leftIndex);
1265+
rightMask |= bit(rightIndex);
1266+
}
1267+
return 0;
1268+
}
1269+
1270+
private boolean isBoundGuardFactor(int factorIndex, long previousMask) {
1271+
if (isBindingSetAssignment(factorIndex)) {
1272+
return false;
1273+
}
1274+
long factorVars = runtimeVarMasks[factorIndex];
1275+
if (factorVars == 0L) {
1276+
return false;
1277+
}
1278+
long previousBound = boundVariableMask(previousMask);
1279+
return (factorVars & previousBound) != 0L && (factorVars & ~previousBound) == 0L;
1280+
}
1281+
1282+
private int compareBridgeUnlockOrder(StatePlan left, StatePlan right) {
1283+
int size = Math.min(left.order().size(), right.order().size());
1284+
long leftMask = 0L;
1285+
long rightMask = 0L;
1286+
for (int i = 0; i < size; i++) {
1287+
int leftIndex = left.order().get(i).intValue();
1288+
int rightIndex = right.order().get(i).intValue();
1289+
if (leftIndex != rightIndex) {
1290+
boolean leftBridge = unlocksFutureJoin(leftIndex, leftMask);
1291+
boolean rightBridge = unlocksFutureJoin(rightIndex, rightMask);
1292+
if (leftBridge == rightBridge) {
1293+
return 0;
1294+
}
1295+
boolean leftLeaf = isLeafUnlock(leftIndex, leftMask);
1296+
boolean rightLeaf = isLeafUnlock(rightIndex, rightMask);
1297+
if (leftBridge && rightLeaf
1298+
&& bridgeUnlockStepComparable(left.physicalStepRanks().get(i),
1299+
right.physicalStepRanks().get(i))) {
1300+
return -1;
1301+
}
1302+
if (rightBridge && leftLeaf
1303+
&& bridgeUnlockStepComparable(right.physicalStepRanks().get(i),
1304+
left.physicalStepRanks().get(i))) {
1305+
return 1;
1306+
}
1307+
return 0;
1308+
}
1309+
leftMask |= bit(leftIndex);
1310+
rightMask |= bit(rightIndex);
1311+
}
1312+
return 0;
1313+
}
1314+
1315+
private boolean unlocksFutureJoin(int factorIndex, long previousMask) {
1316+
return futureRuntimeJoinConnectionCount(factorIndex, previousMask | bit(factorIndex), previousMask) > 0;
1317+
}
1318+
1319+
private boolean isLeafUnlock(int factorIndex, long previousMask) {
1320+
long nextMask = previousMask | bit(factorIndex);
1321+
return futureRuntimeJoinConnectionCount(factorIndex, nextMask, previousMask) == 0
1322+
&& futureDeferredFilterConnectionCount(factorIndex, nextMask, previousMask) == 0;
1323+
}
1324+
1325+
private int futureRuntimeJoinConnectionCount(int factorIndex, long nextMask, long previousMask) {
1326+
long previousBound = runtimeBoundVariableMask(previousMask);
1327+
long introducedVars = runtimeVarMasks[factorIndex] & ~previousBound;
1328+
if (introducedVars == 0L) {
1329+
return 0;
1330+
}
1331+
1332+
long nextBound = runtimeBoundVariableMask(nextMask);
1333+
int futureConnections = 0;
1334+
for (int i = 0; i < factors.size(); i++) {
1335+
if (!contains(nextMask, i)
1336+
&& (runtimeVarMasks[i] & introducedVars) != 0L
1337+
&& (runtimeVarMasks[i] & ~nextBound) != 0L) {
1338+
futureConnections++;
1339+
}
1340+
}
1341+
return futureConnections;
1342+
}
1343+
1344+
private long runtimeBoundVariableMask(long mask) {
1345+
long bound = initiallyBoundVarMask;
1346+
for (int i = 0; i < factors.size(); i++) {
1347+
if (contains(mask, i)) {
1348+
bound |= runtimeVarMasks[i];
1349+
}
1350+
}
1351+
return bound;
1352+
}
1353+
1354+
private boolean bridgeUnlockStepComparable(PhysicalStepRank bridgeStep, PhysicalStepRank leafStep) {
1355+
if (!isFiniteNonNegative(bridgeStep.workRows()) || !isFiniteNonNegative(leafStep.workRows())) {
1356+
return false;
1357+
}
1358+
return bridgeStep.workRows() <= leafStep.workRows() * BRIDGE_UNLOCK_MAX_STEP_WORK_RATIO
1359+
+ STRUCTURAL_WORK_EQUIVALENCE_ROWS;
1360+
}
1361+
12271362
private List<String> newlyUnlockedFilterLabels(long previousMask, long nextMask) {
12281363
if (deferredFilters.isEmpty()) {
12291364
return List.of();
@@ -1507,8 +1642,25 @@ private boolean isBetter(StatePlan candidate, StatePlan incumbent) {
15071642
if (incumbent == null) {
15081643
return true;
15091644
}
1645+
boolean structurallyComparableWork = structurallyComparableWork(candidate.totalWork(), incumbent.totalWork());
1646+
if (structurallyComparableWork && candidate.mask() == incumbent.mask()) {
1647+
int deferredFilterComparison = compareDeferredFilterOrder(candidate.order(), incumbent.order());
1648+
if (deferredFilterComparison != 0) {
1649+
return deferredFilterComparison < 0;
1650+
}
1651+
int boundGuardComparison = compareBoundGuardOrder(candidate.order(), incumbent.order());
1652+
if (boundGuardComparison != 0) {
1653+
return boundGuardComparison < 0;
1654+
}
1655+
}
1656+
if (candidate.mask() == incumbent.mask()) {
1657+
int bridgeUnlockComparison = compareBridgeUnlockOrder(candidate, incumbent);
1658+
if (bridgeUnlockComparison != 0) {
1659+
return bridgeUnlockComparison < 0;
1660+
}
1661+
}
15101662
int connectivityComparison = compareOrderConnectivity(candidate.order(), incumbent.order());
1511-
if (connectivityComparison != 0 && structurallyComparableWork(candidate.totalWork(), incumbent.totalWork())) {
1663+
if (connectivityComparison != 0 && structurallyComparableWork) {
15121664
return connectivityComparison < 0;
15131665
}
15141666
int workComparison = Double.compare(candidate.totalWork(), incumbent.totalWork());
@@ -1536,6 +1688,75 @@ private boolean structurallyComparableWork(double leftWorkRows, double rightWork
15361688
return Math.abs(leftWorkRows - rightWorkRows) <= STRUCTURAL_WORK_EQUIVALENCE_ROWS;
15371689
}
15381690

1691+
private int compareDeferredFilterOrder(List<Integer> left, List<Integer> right) {
1692+
if (deferredFilters.isEmpty()) {
1693+
return 0;
1694+
}
1695+
DeferredFilterOrderRank leftRank = deferredFilterOrderRank(left);
1696+
DeferredFilterOrderRank rightRank = deferredFilterOrderRank(right);
1697+
if (leftRank.filterCount() == 0 || rightRank.filterCount() == 0) {
1698+
return 0;
1699+
}
1700+
int unlockComparison = compareFiniteAscending(leftRank.unlockScore(), rightRank.unlockScore());
1701+
if (unlockComparison != 0) {
1702+
return unlockComparison;
1703+
}
1704+
return compareFiniteAscending(leftRank.windowScore(), rightRank.windowScore());
1705+
}
1706+
1707+
private DeferredFilterOrderRank deferredFilterOrderRank(List<Integer> order) {
1708+
long orderBoundVars = initiallyBoundVarMask;
1709+
for (Integer factorIndex : order) {
1710+
orderBoundVars |= bindingVarMasks[factorIndex.intValue()];
1711+
}
1712+
1713+
int filterCount = 0;
1714+
double unlockScore = 0.0d;
1715+
double windowScore = 0.0d;
1716+
for (int i = 0; i < deferredFilters.size(); i++) {
1717+
long requiredVars = deferredFilterRequiredVarMasks[i];
1718+
if (requiredVars == 0L
1719+
|| (initiallyBoundVarMask & requiredVars) == requiredVars
1720+
|| (orderBoundVars & requiredVars) != requiredVars) {
1721+
continue;
1722+
}
1723+
DeferredFilterPlacement placement = deferredFilterPlacement(order, requiredVars);
1724+
double weight = deferredFilterOrderingWeight(deferredFilters.get(i));
1725+
if (weight <= 0.0d) {
1726+
continue;
1727+
}
1728+
filterCount++;
1729+
unlockScore += weight * placement.unlockStep();
1730+
windowScore += weight * placement.windowWidth();
1731+
}
1732+
return new DeferredFilterOrderRank(filterCount, unlockScore, windowScore);
1733+
}
1734+
1735+
private DeferredFilterPlacement deferredFilterPlacement(List<Integer> order, long requiredVars) {
1736+
long bound = initiallyBoundVarMask;
1737+
int firstStep = -1;
1738+
for (int step = 0; step < order.size(); step++) {
1739+
long introducedVars = bindingVarMasks[order.get(step).intValue()] & ~bound;
1740+
if (firstStep < 0 && (introducedVars & requiredVars) != 0L) {
1741+
firstStep = step;
1742+
}
1743+
bound |= bindingVarMasks[order.get(step).intValue()];
1744+
if ((bound & requiredVars) == requiredVars) {
1745+
return new DeferredFilterPlacement(step, firstStep < 0 ? 1 : step - firstStep + 1);
1746+
}
1747+
}
1748+
return new DeferredFilterPlacement(order.size(), order.size() + 1);
1749+
}
1750+
1751+
private double deferredFilterOrderingWeight(JoinOrderPlanner.FilterConstraint filter) {
1752+
double passRatio = filter.getEstimatedPassRatio();
1753+
if (Double.isFinite(passRatio) && passRatio >= 0.0d
1754+
&& passRatio <= DEFERRED_FILTER_ORDERING_MAX_PASS_RATIO) {
1755+
return 1.0d + (DEFERRED_FILTER_ORDERING_MAX_PASS_RATIO - passRatio);
1756+
}
1757+
return 0.0d;
1758+
}
1759+
15391760
private int comparePhysicalStepOrder(StatePlan left, StatePlan right) {
15401761
if (factorCostModel == null) {
15411762
return 0;
@@ -1638,9 +1859,12 @@ private int futureJoinConnectionCount(int factorIndex, long nextMask, long previ
16381859
return 0;
16391860
}
16401861

1862+
long nextBound = boundVariableMask(nextMask);
16411863
int futureConnections = 0;
16421864
for (int i = 0; i < factors.size(); i++) {
1643-
if (!contains(nextMask, i) && (joinVarMasks[i] & introducedVars) != 0L) {
1865+
if (!contains(nextMask, i)
1866+
&& (joinVarMasks[i] & introducedVars) != 0L
1867+
&& (bindingVarMasks[i] & ~nextBound) != 0L) {
16441868
futureConnections++;
16451869
}
16461870
}
@@ -1660,10 +1884,16 @@ private int futureDeferredFilterConnectionCount(int factorIndex, long nextMask,
16601884
long nextBound = boundVariableMask(nextMask);
16611885
int futureConnections = 0;
16621886
for (int i = 0; i < deferredFilters.size(); i++) {
1887+
if (deferredFilterOrderingWeight(deferredFilters.get(i)) <= 0.0d) {
1888+
continue;
1889+
}
16631890
long requiredVars = deferredFilterRequiredVarMasks[i];
16641891
if ((previousBound & requiredVars) == requiredVars
1665-
|| (requiredVars & introducedVars) == 0L
1666-
|| (nextBound & requiredVars) == requiredVars) {
1892+
|| (requiredVars & introducedVars) == 0L) {
1893+
continue;
1894+
}
1895+
if ((nextBound & requiredVars) == requiredVars) {
1896+
futureConnections += completedDeferredFilterConnectionWeight(deferredFilters.get(i));
16671897
continue;
16681898
}
16691899
long missingVars = requiredVars & ~nextBound;
@@ -1676,6 +1906,10 @@ private int futureDeferredFilterConnectionCount(int factorIndex, long nextMask,
16761906
return futureConnections;
16771907
}
16781908

1909+
private int completedDeferredFilterConnectionWeight(JoinOrderPlanner.FilterConstraint filter) {
1910+
return deferredFilterOrderingWeight(filter) > 0.0d ? COMPLETED_DEFERRED_FILTER_CONNECTIONS : 0;
1911+
}
1912+
16791913
private static int compareOrder(List<Integer> left, List<Integer> right) {
16801914
int size = Math.min(left.size(), right.size());
16811915
for (int i = 0; i < size; i++) {
@@ -1848,6 +2082,12 @@ private record PhysicalStepRank(boolean comparable, int missingLookupComponents,
18482082
double factorOutputRows, double workRows, int lookupComponents, double accessRowsBeforeFilter) {
18492083
}
18502084

2085+
private record DeferredFilterOrderRank(int filterCount, double unlockScore, double windowScore) {
2086+
}
2087+
2088+
private record DeferredFilterPlacement(int unlockStep, int windowWidth) {
2089+
}
2090+
18512091
private record FactorBuildResult(List<PlanFactor> factors,
18522092
SketchBasedJoinEstimator.SketchPlannerPath rejectionPath,
18532093
TupleExpr rejectedFactor) {

0 commit comments

Comments
 (0)