Skip to content

Commit deba64e

Browse files
committed
improvements
1 parent 4162437 commit deba64e

43 files changed

Lines changed: 2711 additions & 262 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.codex/skills/docker-jfr-benchmark-loop/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ Explicit selector plus params:
4141
- `jdk.CPUTimeSample#enabled=true`
4242
- `report-on-exit=cpu-time-hot-methods`
4343
- This skill wrapper adds the missing fidelity flags:
44-
- `-XX:FlightRecorderOptions=stackdepth=1024,samplethreads=true`
44+
- `-XX:FlightRecorderOptions=stackdepth=1024`
4545
- `-XX:+UnlockDiagnosticVMOptions`
4646
- `-XX:+DebugNonSafepoints`
47+
- The repo helper sets `-XX:StartFlightRecording:method-profiling=max` for CPU-time JFR runs.
4748

4849
## Core loop
4950

.codex/skills/docker-jfr-benchmark-loop/scripts/run-docker-jfr-loop.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Options:
1717
Notes:
1818
- This wrapper always uses scripts/run-single-benchmark-docker.sh.
1919
- JFR CPU-time profiling is already enabled by the repo docker helper.
20-
- This wrapper injects stackdepth/samplethreads plus DebugNonSafepoints fidelity flags.
20+
- This wrapper injects stackdepth plus DebugNonSafepoints fidelity flags.
2121
- Raw JMH args are intentionally blocked. Use --param or --jvm-arg instead.
2222
USAGE
2323
}
@@ -135,7 +135,7 @@ if [[ -z "${benchmark_id}" && ( -z "${module}" || -z "${benchmark_class}" || -z
135135
fi
136136

137137
default_jvm_args=(
138-
"-XX:FlightRecorderOptions=stackdepth=1024,samplethreads=true"
138+
"-XX:FlightRecorderOptions=stackdepth=1024"
139139
"-XX:+UnlockDiagnosticVMOptions"
140140
"-XX:+DebugNonSafepoints"
141141
)

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,6 @@ e2e/test-results
5858
/.codex/environments/environment.toml
5959
improved-optimizers-query-rewrite-sketch-based-lmdb-page-walking/
6060
/.m2_repo_linux_j25/
61+
/.m2_repo_linux_j26/
6162
/core/sail/lmdb/persistent-lmdb-theme-store/
6263
core/sail/lmdb/persistent-lmdb-theme-store

core/query/src/main/java/org/eclipse/rdf4j/query/explanation/GenericPlanNode.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -881,13 +881,17 @@ private void appendCostAnnotation(StringBuilder sb) {
881881
}
882882

883883
if (!metrics.isEmpty()) {
884-
sb.append(" (")
885-
.append(metrics.entrySet()
886-
.stream()
887-
.map(e -> e.getKey() + "=" + e.getValue())
888-
.reduce((a, b) -> a + ", " + b)
889-
.orElse(""))
890-
.append(")");
884+
sb.append(" (");
885+
boolean first = true;
886+
for (Map.Entry<String, String> metric : metrics.entrySet()) {
887+
if (first) {
888+
first = false;
889+
} else {
890+
sb.append(", ");
891+
}
892+
sb.append(metric.getKey()).append('=').append(metric.getValue());
893+
}
894+
sb.append(")");
891895
}
892896
}
893897

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/TripleSource.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*
99
* SPDX-License-Identifier: BSD-3-Clause
1010
*******************************************************************************/
11+
// Some portions generated by Codex
1112
package org.eclipse.rdf4j.query.algebra.evaluation;
1213

1314
import java.util.Comparator;
@@ -49,6 +50,29 @@ public interface TripleSource extends AvailableStatementOrder {
4950
CloseableIteration<? extends Statement> getStatements(Resource subj, IRI pred,
5051
Value obj, Resource... contexts) throws QueryEvaluationException;
5152

53+
/**
54+
* Counts statements matching the supplied pattern without requiring callers to materialize converted bindings.
55+
*
56+
* @param subj A Resource specifying the subject, or <var>null</var> for a wildcard.
57+
* @param pred A IRI specifying the predicate, or <var>null</var> for a wildcard.
58+
* @param obj A Value specifying the object, or <var>null</var> for a wildcard.
59+
* @param contexts The context(s) to get the statements from. Note that this parameter is a vararg and as such is
60+
* optional. If no contexts are supplied the method operates on the entire repository.
61+
* @return The number of relevant statements.
62+
* @throws QueryEvaluationException If the triple source failed to count the statements.
63+
*/
64+
default long getStatementCount(Resource subj, IRI pred, Value obj, Resource... contexts)
65+
throws QueryEvaluationException {
66+
long count = 0;
67+
try (CloseableIteration<? extends Statement> statements = getStatements(subj, pred, obj, contexts)) {
68+
while (statements.hasNext()) {
69+
statements.next();
70+
count++;
71+
}
72+
}
73+
return count;
74+
}
75+
5276
/**
5377
* Gets all statements that have a specific subject, predicate and/or object. All three parameters may be null to
5478
* indicate wildcards. Optionally a (set of) context(s) may be specified in which case the result will be restricted

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/evaluationsteps/BindingSetAssignmentQueryEvaluationStep.java

Lines changed: 113 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
*
99
* SPDX-License-Identifier: BSD-3-Clause
1010
*******************************************************************************/
11+
// Some portions generated by Codex
1112
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps;
1213

14+
import java.util.HashMap;
1315
import java.util.Iterator;
16+
import java.util.Map;
17+
import java.util.function.BiConsumer;
1418
import java.util.function.Function;
19+
import java.util.function.Predicate;
1520

1621
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
1722
import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration;
@@ -29,10 +34,21 @@
2934
public class BindingSetAssignmentQueryEvaluationStep implements QueryEvaluationStep {
3035
private final BindingSetAssignment node;
3136
private final Function<BindingSet, MutableBindingSet> bsMaker;
37+
private final BindingNameAccess[] bindingNames;
38+
private final Map<String, BindingNameAccess> bindingNamesByName;
3239

3340
public BindingSetAssignmentQueryEvaluationStep(BindingSetAssignment node, QueryEvaluationContext context) {
3441
this.node = node;
3542
bsMaker = context::createBindingSet;
43+
bindingNames = node.getBindingNames()
44+
.stream()
45+
.map(bindingName -> new BindingNameAccess(bindingName, context))
46+
.toArray(BindingNameAccess[]::new);
47+
Map<String, BindingNameAccess> bindingNamesByName = new HashMap<>(bindingNames.length * 2);
48+
for (BindingNameAccess bindingName : bindingNames) {
49+
bindingNamesByName.put(bindingName.name, bindingName);
50+
}
51+
this.bindingNamesByName = Map.copyOf(bindingNamesByName);
3652
}
3753

3854
@Override
@@ -44,45 +60,92 @@ public CloseableIteration<BindingSet> evaluate(BindingSet bindings) {
4460
result = new CloseableIteratorIteration<>(assignments);
4561
} else {
4662
// we need to verify that new binding assignments do not overwrite existing bindings
47-
result = new LookAheadIteration<>() {
63+
final boolean hasParentOverlap = hasParentOverlap(bindings);
64+
if (!hasParentOverlap) {
65+
result = new LookAheadIteration<>() {
4866

49-
@Override
50-
protected BindingSet getNextElement() throws QueryEvaluationException {
51-
MutableBindingSet nextResult = null;
52-
while (nextResult == null && assignments.hasNext()) {
53-
final BindingSet assignedBindings = assignments.next();
67+
@Override
68+
protected BindingSet getNextElement() throws QueryEvaluationException {
69+
if (!assignments.hasNext()) {
70+
return null;
71+
}
72+
BindingSet assignedBindings = assignments.next();
73+
if (assignedBindings.isEmpty()) {
74+
return bindings;
75+
}
5476

77+
MutableBindingSet nextResult = bsMaker.apply(bindings);
5578
for (String name : assignedBindings.getBindingNames()) {
56-
if (nextResult == null) {
57-
nextResult = bsMaker.apply(bindings);
58-
}
59-
6079
final Value assignedValue = assignedBindings.getValue(name);
6180
if (assignedValue != null) {
62-
// check that the binding assignment does not overwrite existing bindings.
63-
Value existingValue = bindings.getValue(name);
64-
if (existingValue == null || assignedValue.equals(existingValue)) {
65-
if (existingValue == null) {
66-
// we are not overwriting an existing binding.
67-
nextResult.addBinding(name, assignedValue);
68-
}
81+
BindingNameAccess bindingName = bindingNamesByName.get(name);
82+
if (bindingName == null) {
83+
nextResult.addBinding(name, assignedValue);
6984
} else {
70-
// if values are not equal there is no compatible merge and we should return no
71-
// next element.
72-
nextResult = null;
73-
break;
85+
bindingName.addBinding.accept(assignedValue, nextResult);
7486
}
7587
}
7688
}
89+
return nextResult;
7790
}
78-
return nextResult;
79-
}
8091

81-
@Override
82-
protected void handleClose() {
92+
@Override
93+
protected void handleClose() {
8394

84-
}
85-
};
95+
}
96+
};
97+
} else {
98+
result = new LookAheadIteration<>() {
99+
100+
@Override
101+
protected BindingSet getNextElement() throws QueryEvaluationException {
102+
MutableBindingSet nextResult = null;
103+
while (nextResult == null && assignments.hasNext()) {
104+
final BindingSet assignedBindings = assignments.next();
105+
106+
for (String name : assignedBindings.getBindingNames()) {
107+
if (nextResult == null) {
108+
nextResult = bsMaker.apply(bindings);
109+
}
110+
111+
final Value assignedValue = assignedBindings.getValue(name);
112+
if (assignedValue != null) {
113+
BindingNameAccess bindingName = bindingNamesByName.get(name);
114+
115+
// check that the binding assignment does not overwrite existing bindings.
116+
Value existingValue = bindingName == null ? bindings.getValue(name)
117+
: bindingName.getValue.apply(bindings);
118+
if (existingValue == null || assignedValue.equals(existingValue)) {
119+
if (existingValue == null) {
120+
// we are not overwriting an existing binding.
121+
if (bindingName == null) {
122+
nextResult.addBinding(name, assignedValue);
123+
} else {
124+
bindingName.addBinding.accept(assignedValue, nextResult);
125+
}
126+
}
127+
} else {
128+
// if values are not equal there is no compatible merge and we should return no
129+
// next element.
130+
nextResult = null;
131+
break;
132+
}
133+
}
134+
}
135+
}
136+
return nextResult;
137+
}
138+
139+
@Override
140+
protected void handleClose() {
141+
142+
}
143+
};
144+
}
145+
}
146+
147+
if (!node.isRuntimeTelemetryEnabled()) {
148+
return result;
86149
}
87150

88151
return new IterationWrapper<>(result) {
@@ -107,4 +170,27 @@ protected void handleClose() {
107170
}
108171
};
109172
}
173+
174+
private boolean hasParentOverlap(BindingSet bindings) {
175+
for (BindingNameAccess bindingName : bindingNames) {
176+
if (bindingName.hasBinding.test(bindings)) {
177+
return true;
178+
}
179+
}
180+
return false;
181+
}
182+
183+
private static final class BindingNameAccess {
184+
private final String name;
185+
private final Predicate<BindingSet> hasBinding;
186+
private final Function<BindingSet, Value> getValue;
187+
private final BiConsumer<Value, MutableBindingSet> addBinding;
188+
189+
private BindingNameAccess(String name, QueryEvaluationContext context) {
190+
this.name = name;
191+
hasBinding = context.hasBinding(name);
192+
getValue = context.getValue(name);
193+
addBinding = context.addBinding(name);
194+
}
195+
}
110196
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
// Some portions generated by Codex
12+
package org.eclipse.rdf4j.query.algebra.evaluation.impl.evaluationsteps;
13+
14+
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
15+
import org.eclipse.rdf4j.common.iteration.LookAheadIteration;
16+
import org.eclipse.rdf4j.query.BindingSet;
17+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
18+
19+
final class BoundStatementPatternGuardJoinIteration extends LookAheadIteration<BindingSet> {
20+
21+
@FunctionalInterface
22+
interface GuardCounter {
23+
long count(BindingSet bindings);
24+
}
25+
26+
private final CloseableIteration<BindingSet> leftIteration;
27+
private final GuardCounter guardCounter;
28+
private final QueryEvaluationStep fallbackRightPrepared;
29+
30+
private CloseableIteration<BindingSet> fallbackIteration;
31+
private BindingSet repeatedBindings;
32+
private long remainingRepeats;
33+
34+
BoundStatementPatternGuardJoinIteration(CloseableIteration<BindingSet> leftIteration, GuardCounter guardCounter,
35+
QueryEvaluationStep fallbackRightPrepared) {
36+
this.leftIteration = leftIteration;
37+
this.guardCounter = guardCounter;
38+
this.fallbackRightPrepared = fallbackRightPrepared;
39+
}
40+
41+
@Override
42+
protected BindingSet getNextElement() {
43+
while (true) {
44+
if (remainingRepeats > 0) {
45+
remainingRepeats--;
46+
return repeatedBindings;
47+
}
48+
repeatedBindings = null;
49+
50+
if (fallbackIteration != null) {
51+
if (fallbackIteration.hasNext()) {
52+
return fallbackIteration.next();
53+
}
54+
fallbackIteration.close();
55+
fallbackIteration = null;
56+
}
57+
58+
if (!leftIteration.hasNext()) {
59+
return null;
60+
}
61+
62+
BindingSet leftBindings = leftIteration.next();
63+
long statementCount = guardCounter.count(leftBindings);
64+
if (statementCount < 0) {
65+
fallbackIteration = fallbackRightPrepared.evaluate(leftBindings);
66+
continue;
67+
}
68+
if (statementCount > 0) {
69+
repeatedBindings = leftBindings;
70+
remainingRepeats = statementCount - 1;
71+
return leftBindings;
72+
}
73+
}
74+
}
75+
76+
@Override
77+
protected void handleClose() {
78+
try {
79+
if (fallbackIteration != null) {
80+
fallbackIteration.close();
81+
}
82+
} finally {
83+
leftIteration.close();
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)