Skip to content

Commit 39a800d

Browse files
committed
perf: GH-5715 optimize query evaluation hot paths
1 parent a32c67a commit 39a800d

7 files changed

Lines changed: 343 additions & 47 deletions

File tree

core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/DistinctIteration.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,7 @@ public DistinctIteration(CloseableIteration<? extends E> iter, Supplier<Set<E>>
7676
*/
7777
@Override
7878
protected boolean accept(E object) {
79-
if (inExcludeSet(object)) {
80-
// object has already been returned
81-
return false;
82-
} else {
83-
add(object);
84-
return true;
85-
}
79+
return add(object);
8680
}
8781

8882
@Override

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

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

14+
import java.lang.invoke.MethodHandles;
15+
import java.lang.invoke.VarHandle;
1316
import java.util.ArrayList;
1417
import java.util.Arrays;
1518
import java.util.Collections;
@@ -43,9 +46,24 @@ public class ArrayBindingSet extends AbstractBindingSet implements MutableBindin
4346

4447
private static final long serialVersionUID = -1L;
4548

49+
@FunctionalInterface
50+
public interface SortedBindingNamesCache {
51+
List<String> get(long activeBindingMask);
52+
}
53+
4654
private static final Logger logger = LoggerFactory.getLogger(ArrayBindingSet.class);
4755
private static final Value NULL_VALUE = Values
4856
.iri("urn:null:d57c56f3-41a9-468e-8dce-5706ebdef84c_e88d9e52-27cb-4056-a889-1ea353fa6f0c");
57+
private static final VarHandle LAST_EQUALS_MISMATCH_INDEX;
58+
private static int lastEqualsMismatchIndex;
59+
static {
60+
try {
61+
LAST_EQUALS_MISMATCH_INDEX = MethodHandles.lookup()
62+
.findStaticVarHandle(ArrayBindingSet.class, "lastEqualsMismatchIndex", int.class);
63+
} catch (ReflectiveOperationException e) {
64+
throw new ExceptionInInitializerError(e);
65+
}
66+
}
4967

5068
private final String[] bindingNames;
5169

@@ -54,6 +72,9 @@ public class ArrayBindingSet extends AbstractBindingSet implements MutableBindin
5472
private boolean empty;
5573

5674
private final Value[] values;
75+
private final SortedBindingNamesCache sharedSortedBindingNamesCache;
76+
private long activeBindingMask;
77+
private int cachedSize = -1;
5778
private int cachedHashCode;
5879

5980
/**
@@ -64,16 +85,29 @@ public class ArrayBindingSet extends AbstractBindingSet implements MutableBindin
6485
* @param names The binding names.
6586
*/
6687
public ArrayBindingSet(String... names) {
88+
this(names, null);
89+
}
90+
91+
public ArrayBindingSet(String[] names, SortedBindingNamesCache sharedSortedBindingNamesCache) {
6792
this.bindingNames = names;
6893
this.values = new Value[names.length];
94+
this.sharedSortedBindingNamesCache = sharedSortedBindingNamesCache;
6995
this.empty = true;
96+
this.cachedSize = 0;
7097
}
7198

7299
public ArrayBindingSet(BindingSet toCopy, Set<String> names, String[] namesArray) {
100+
this(toCopy, names, namesArray, null);
101+
}
102+
103+
public ArrayBindingSet(BindingSet toCopy, Set<String> names, String[] namesArray,
104+
SortedBindingNamesCache sharedSortedBindingNamesCache) {
73105
assert !(toCopy instanceof ArrayBindingSet);
74106

75107
this.bindingNames = namesArray;
76108
this.values = new Value[this.bindingNames.length];
109+
this.sharedSortedBindingNamesCache = sharedSortedBindingNamesCache;
110+
int size = 0;
77111
for (int i = 0; i < this.bindingNames.length; i++) {
78112
Binding binding = toCopy.getBinding(this.bindingNames[i]);
79113

@@ -82,20 +116,36 @@ public ArrayBindingSet(BindingSet toCopy, Set<String> names, String[] namesArray
82116
if (this.values[i] == null) {
83117
this.values[i] = NULL_VALUE;
84118
}
119+
size++;
85120
} else if (hasBinding(this.bindingNames[i])) {
86121
this.values[i] = NULL_VALUE;
122+
size++;
87123
}
88124
}
89125
this.empty = toCopy.isEmpty();
126+
this.cachedSize = this.empty ? 0 : size;
127+
if (sharedSortedBindingNamesCache != null) {
128+
this.activeBindingMask = calculateActiveBindingMask();
129+
}
90130
assert !this.empty || size() == 0;
91131

92132
}
93133

94134
public ArrayBindingSet(ArrayBindingSet toCopy, String... names) {
135+
this(toCopy, names, null);
136+
}
137+
138+
public ArrayBindingSet(ArrayBindingSet toCopy, String[] names,
139+
SortedBindingNamesCache sharedSortedBindingNamesCache) {
95140
this.bindingNames = names;
96141

97142
this.values = Arrays.copyOf(toCopy.values, toCopy.values.length);
143+
this.sharedSortedBindingNamesCache = sharedSortedBindingNamesCache;
98144
this.empty = toCopy.empty;
145+
this.cachedSize = toCopy.cachedSize;
146+
if (sharedSortedBindingNamesCache != null) {
147+
this.activeBindingMask = calculateActiveBindingMask();
148+
}
99149
assert !this.empty || size() == 0;
100150
}
101151

@@ -117,6 +167,7 @@ public BiConsumer<Value, ArrayBindingSet> getDirectSetBinding(String bindingName
117167
return (v, a) -> {
118168
a.values[index] = v == null ? NULL_VALUE : v;
119169
a.empty = false;
170+
a.updateActiveBindingMask(index, true);
120171
a.clearCache();
121172
};
122173
}
@@ -132,6 +183,7 @@ public BiConsumer<Value, ArrayBindingSet> getDirectAddBinding(String bindingName
132183
assert a.values[index] == null;
133184
a.values[index] = v == null ? NULL_VALUE : v;
134185
a.empty = false;
186+
a.updateActiveBindingMask(index, true);
135187
a.clearCache();
136188
};
137189

@@ -280,18 +332,23 @@ public Iterator<Binding> iterator() {
280332
@Override
281333
public int size() {
282334
if (isEmpty()) {
335+
cachedSize = 0;
283336
return 0;
284337
}
285338

286-
int size = 0;
339+
if (cachedSize == -1) {
340+
int size = 0;
287341

288-
for (Value value : values) {
289-
if (value != null) {
290-
size++;
342+
for (Value value : values) {
343+
if (value != null) {
344+
size++;
345+
}
291346
}
347+
348+
cachedSize = size;
292349
}
293350

294-
return size;
351+
return cachedSize;
295352
}
296353

297354
@Override
@@ -355,23 +412,28 @@ private boolean slowIsCompatible(BindingSet other) {
355412
public List<String> getSortedBindingNames() {
356413

357414
if (sortedBindingNames == null) {
358-
int size = size();
359-
360-
if (size == 1) {
361-
for (int i = 0; i < bindingNames.length; i++) {
362-
if (values[i] != null) {
363-
sortedBindingNames = Collections.singletonList(bindingNames[i]);
364-
}
365-
}
415+
if (sharedSortedBindingNamesCache != null) {
416+
sortedBindingNames = sharedSortedBindingNamesCache.get(activeBindingMask);
366417
} else {
367-
ArrayList<String> names = new ArrayList<>(size);
368-
for (int i = 0; i < bindingNames.length; i++) {
369-
if (values[i] != null) {
370-
names.add(bindingNames[i]);
418+
int size = size();
419+
420+
if (size == 1) {
421+
for (int i = 0; i < bindingNames.length; i++) {
422+
if (values[i] != null) {
423+
sortedBindingNames = Collections.singletonList(bindingNames[i]);
424+
break;
425+
}
426+
}
427+
} else {
428+
ArrayList<String> names = new ArrayList<>(size);
429+
for (int i = 0; i < bindingNames.length; i++) {
430+
if (values[i] != null) {
431+
names.add(bindingNames[i]);
432+
}
371433
}
434+
names.sort(String::compareTo);
435+
sortedBindingNames = names;
372436
}
373-
names.sort(String::compareTo);
374-
sortedBindingNames = names;
375437
}
376438
}
377439

@@ -393,6 +455,7 @@ public void addBinding(Binding binding) {
393455
assert this.values[index] == null;
394456
this.values[index] = value == null ? NULL_VALUE : value;
395457
empty = false;
458+
updateActiveBindingMask(index, true);
396459
clearCache();
397460
}
398461

@@ -405,6 +468,7 @@ public void setBinding(Binding binding) {
405468
Value value = binding.getValue();
406469
this.values[index] = value == null ? NULL_VALUE : value;
407470
empty = false;
471+
updateActiveBindingMask(index, true);
408472
clearCache();
409473
}
410474

@@ -416,6 +480,7 @@ public void setBinding(String name, Value value) {
416480
}
417481

418482
this.values[index] = value;
483+
updateActiveBindingMask(index, value != null);
419484
if (value == null) {
420485
this.empty = true;
421486
for (Value value1 : this.values) {
@@ -437,6 +502,8 @@ public boolean isEmpty() {
437502

438503
private void clearCache() {
439504
bindingNamesSetCache = null;
505+
sortedBindingNames = null;
506+
cachedSize = -1;
440507
cachedHashCode = 0;
441508
}
442509

@@ -459,7 +526,33 @@ public void addAll(ArrayBindingSet other) {
459526
}
460527

461528
clearCache();
529+
if (sharedSortedBindingNamesCache != null) {
530+
activeBindingMask = calculateActiveBindingMask();
531+
}
532+
533+
}
534+
535+
private void updateActiveBindingMask(int index, boolean hasValue) {
536+
if (sharedSortedBindingNamesCache == null) {
537+
return;
538+
}
539+
540+
long maskBit = 1L << index;
541+
if (hasValue) {
542+
activeBindingMask |= maskBit;
543+
} else {
544+
activeBindingMask &= ~maskBit;
545+
}
546+
}
462547

548+
private long calculateActiveBindingMask() {
549+
long mask = 0L;
550+
for (int i = 0; i < values.length; i++) {
551+
if (values[i] != null) {
552+
mask |= 1L << i;
553+
}
554+
}
555+
return mask;
463556
}
464557

465558
@Override
@@ -495,9 +588,20 @@ public boolean equals(Object other) {
495588
}
496589

497590
if (bindingNames == o.bindingNames) {
498-
for (int i = 0; i < values.length; i++) {
499-
if (values[i] != o.values[i]) {
500-
if (!Objects.equals(values[i], o.values[i])) {
591+
int valuesLength = values.length;
592+
int startIndex = (int) LAST_EQUALS_MISMATCH_INDEX.getOpaque();
593+
if (startIndex >= valuesLength) {
594+
startIndex = 0;
595+
}
596+
597+
for (int i = 0; i < valuesLength; i++) {
598+
int index = startIndex + i;
599+
if (index >= valuesLength) {
600+
index -= valuesLength;
601+
}
602+
if (values[index] != o.values[index]) {
603+
if (!Objects.equals(values[index], o.values[index])) {
604+
LAST_EQUALS_MISMATCH_INDEX.setOpaque(index);
501605
return false;
502606
}
503607
}

0 commit comments

Comments
 (0)