Skip to content

Commit bf240ac

Browse files
Merge pull request #962 from sirixdb/perf/array-slice-fixes
perf: O(N) array-slice scan + primitive cursor cache
2 parents 8b3a008 + 5e3f9fe commit bf240ac

3 files changed

Lines changed: 313 additions & 25 deletions

File tree

bundles/sirix-query/src/main/java/io/sirix/query/json/AbstractJsonDBArray.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ public JsonNodeReadOnlyTrx getTrx() {
9999
return rtx;
100100
}
101101

102+
protected final JsonItemFactory getJsonItemFactory() {
103+
return jsonItemFactory;
104+
}
105+
102106
@Override
103107
public Array replaceAt(int index, Sequence value) {
104108
modify(index, value, Op.Replace);

bundles/sirix-query/src/main/java/io/sirix/query/json/JsonDBArraySlice.java

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.sirix.axis.temporal.PrefetchedAllTimeAxis;
1515
import io.sirix.axis.temporal.PrefetchedFutureAxis;
1616
import io.sirix.axis.temporal.PrefetchedPastAxis;
17+
import io.sirix.settings.Fixed;
1718

1819
import java.util.ArrayList;
1920
import java.util.List;
@@ -39,6 +40,12 @@ public final class JsonDBArraySlice extends AbstractJsonDBArray<JsonDBArraySlice
3940
*/
4041
private List<Sequence> values;
4142

43+
/** Last slice-relative index served by {@link #at(int)} / {@link #at(IntNumeric)}. */
44+
private int cursorSliceIndex = -1;
45+
46+
/** Node key positioned at {@link #cursorSliceIndex}; {@link Fixed#NULL_NODE_KEY} if invalid. */
47+
private long cursorNodeKey = Fixed.NULL_NODE_KEY.getStandardProperty();
48+
4249
/**
4350
* Constructor.
4451
*
@@ -59,13 +66,14 @@ public JsonDBArraySlice(final JsonNodeReadOnlyTrx rtx, final JsonDBCollection co
5966

6067
assert this.rtx.isArray();
6168

62-
jsonUtil = new JsonItemFactory();
69+
jsonUtil = getJsonItemFactory();
6370

64-
if ((fromIndex < 0) || (fromIndex > toIndex) || (fromIndex >= this.rtx.getChildCount())) {
71+
final long childCount = this.rtx.getChildCount();
72+
if ((fromIndex < 0) || (fromIndex > toIndex) || (fromIndex >= childCount)) {
6573
throw new QueryException(ErrorCode.ERR_INVALID_ARGUMENT_TYPE, "Invalid array start index: %s", fromIndex);
6674
}
6775

68-
if (toIndex > this.rtx.getChildCount()) {
76+
if (toIndex > childCount) {
6977
throw new QueryException(ErrorCode.ERR_INVALID_ARGUMENT_TYPE, "Invalid array end index: %s", toIndex);
7078
}
7179

@@ -117,55 +125,95 @@ public List<Sequence> values() {
117125
}
118126

119127
private List<Sequence> getValues() {
120-
final var values = new ArrayList<Sequence>();
128+
final int length = toIndex - fromIndex;
129+
final ArrayList<Sequence> out = new ArrayList<>(length);
130+
if (length == 0) {
131+
return out;
132+
}
133+
134+
final ChildAxis axis = new ChildAxis(rtx);
121135

122-
for (int i = 0, length = len(); i < length; i++) {
123-
values.add(at(fromIndex + i));
136+
for (int skipped = 0; skipped < fromIndex; skipped++) {
137+
if (!axis.hasNext()) {
138+
return out;
139+
}
140+
axis.nextLong();
124141
}
125142

126-
return values;
143+
for (int collected = 0; collected < length; collected++) {
144+
if (!axis.hasNext()) {
145+
break;
146+
}
147+
axis.nextLong();
148+
out.add(jsonUtil.getSequence(rtx, collection));
149+
}
150+
151+
invalidateCursor();
152+
return out;
127153
}
128154

129-
private Sequence getSequenceAtIndex(final JsonNodeReadOnlyTrx rtx, final int index) {
130-
moveRtx();
155+
private Sequence sequenceAtSliceIndex(final int sliceIndex) {
156+
final int absoluteIndex = fromIndex + sliceIndex;
157+
final long arrayKey = getNodeKey();
131158

132-
final var axis = new ChildAxis(rtx);
159+
if (cursorSliceIndex >= 0 && sliceIndex == cursorSliceIndex + 1
160+
&& rtx.moveTo(cursorNodeKey) && rtx.getParentKey() == arrayKey
161+
&& rtx.hasRightSibling()) {
162+
rtx.moveToRightSibling();
163+
cursorSliceIndex = sliceIndex;
164+
cursorNodeKey = rtx.getNodeKey();
165+
return jsonUtil.getSequence(rtx, collection);
166+
}
133167

134-
for (int i = 0; i < index && axis.hasNext(); i++)
135-
axis.nextLong();
168+
moveRtx();
169+
final ChildAxis axis = new ChildAxis(rtx);
136170

137-
if (axis.hasNext()) {
171+
for (int i = 0; i < absoluteIndex; i++) {
172+
if (!axis.hasNext()) {
173+
invalidateCursor();
174+
return null;
175+
}
138176
axis.nextLong();
177+
}
139178

140-
return jsonUtil.getSequence(rtx, collection);
179+
if (!axis.hasNext()) {
180+
invalidateCursor();
181+
return null;
141182
}
183+
axis.nextLong();
184+
185+
cursorSliceIndex = sliceIndex;
186+
cursorNodeKey = rtx.getNodeKey();
187+
return jsonUtil.getSequence(rtx, collection);
188+
}
142189

143-
return null;
190+
private void invalidateCursor() {
191+
cursorSliceIndex = -1;
192+
cursorNodeKey = Fixed.NULL_NODE_KEY.getStandardProperty();
144193
}
145194

146195
@Override
147-
public Sequence at(IntNumeric numericIndex) {
148-
int ii = fromIndex + numericIndex.intValue();
149-
if (ii >= toIndex) {
150-
throw new QueryException(ErrorCode.ERR_INVALID_ARGUMENT_TYPE, "Invalid array index: %s", numericIndex.intValue());
196+
public Sequence at(final IntNumeric numericIndex) {
197+
final int sliceIndex = numericIndex.intValue();
198+
if (fromIndex + sliceIndex >= toIndex) {
199+
throw new QueryException(ErrorCode.ERR_INVALID_ARGUMENT_TYPE, "Invalid array index: %s", sliceIndex);
151200
}
152201

153202
if (values == null) {
154-
return getSequenceAtIndex(rtx, ii);
203+
return sequenceAtSliceIndex(sliceIndex);
155204
}
156205

157-
return values.get(ii);
206+
return values.get(sliceIndex);
158207
}
159208

160209
@Override
161-
public Sequence at(int index) {
162-
int ii = fromIndex + index;
163-
if (ii >= toIndex) {
210+
public Sequence at(final int index) {
211+
if (fromIndex + index >= toIndex) {
164212
throw new QueryException(ErrorCode.ERR_INVALID_ARGUMENT_TYPE, "Invalid array index: %s", index);
165213
}
166214

167215
if (values == null) {
168-
return getSequenceAtIndex(rtx, ii);
216+
return sequenceAtSliceIndex(index);
169217
}
170218

171219
return values.get(index);

0 commit comments

Comments
 (0)