Skip to content

perf: O(N) array-slice scan + primitive cursor cache#962

Merged
JohannesLichtenberger merged 1 commit intomainfrom
perf/array-slice-fixes
Apr 29, 2026
Merged

perf: O(N) array-slice scan + primitive cursor cache#962
JohannesLichtenberger merged 1 commit intomainfrom
perf/array-slice-fixes

Conversation

@JohannesLichtenberger
Copy link
Copy Markdown
Member

@JohannesLichtenberger JohannesLichtenberger commented Apr 29, 2026

Summary

Rebased onto current main after the temporal-axis-prefetch and OBJECT_NAMED_* leaf changes landed. The branch is now a single focused commit.

JsonDBArraySlice.getValues() was O(N²): every at(fromIndex+i) rebuilt a ChildAxis and walked from index 0. Replaced with a single forward pass that skips fromIndex siblings and then collects the slice. ArrayList pre-sized to slice width.

Sequential at(i) access was also O(N²) for the same reason. Added a primitive cursor cache (int cursorSliceIndex, long cursorNodeKey); the next monotone access advances one moveToRightSibling instead of restarting. Validated against the slice's array via getParentKey() == arrayKey so structural mutations can't surface a stale element.

Fixed an indexing bug on the cached path of at(IntNumeric)values.get(fromIndex + i) was indexing a slice-local list with a global index, throwing IndexOutOfBoundsException whenever fromIndex > 0 after values() had been called.

Dropped the duplicate JsonItemFactory instantiation by exposing the parent's via a protected getter.

On the dropped parallel materialisation

An earlier revision of this branch added a ForkJoinPool-driven parallel path for atomic-only slices ≥ 262 144 elements, with full Stage-1/2/3 fan-out. After rebasing onto main (which brought in temporal-axis prefetching and fused OBJECT_NAMED_* leaves), the serial path's per-element cost dropped from ~200 ns to ~93 ns. At that work density, rtx-creation + ForkJoinPool fixed costs eat the entire parallel budget — measured 1.00× ratio (no payoff at all) on a 19-way commonPool. Dropped per the standing rule that parallelism in this codebase must demonstrably pay off; if it ever stops paying, remove it.

If a future change makes per-element decode expensive again (e.g., a new compression codec), revisit array-slice-parallelism.md in the project memory — the design proof and obligations are documented there.

Test plan

  • ./gradlew :sirix-query:test --tests "io.sirix.query.json.JsonDBArraySliceTest" — 11 tests, 0 failures.
  • Cursor cache stress (130 sequential reads on a 200-element array, then a backward jump).
  • Indexing-bug regression on cached path.
  • Sub-slicing semantics pinned down (existing absolute-index behaviour preserved as rangeOnSlice_yieldsSubSlice_withCorrectAbsoluteIndices).
  • Bounds-throw paths covered.

@JohannesLichtenberger JohannesLichtenberger changed the base branch from perf/umbra-ballpark-iter-5 to main April 29, 2026 21:17
- getValues(): single ChildAxis pass (skip fromIndex, collect length)
  replaces per-element axis rebuild — O(N^2) -> O(toIndex). ArrayList
  pre-sized to slice width.
- at(int)/at(IntNumeric): primitive (int sliceIdx, long nodeKey) cursor
  cache. Sequential access collapses to one walk + moveToRightSibling
  hops. Cache validated via getParentKey() == arrayKey to stay correct
  across structural mutations.
- at(IntNumeric): fixed indexing bug — cached path used
  values.get(fromIndex+i) into a slice-local list.
- Drop duplicate JsonItemFactory allocation (reuse parent's).

11 unit tests cover regression, sequential/random correctness,
cached/uncached agreement, memoisation, bounds, sub-slicing, and
cursor stress.
@JohannesLichtenberger JohannesLichtenberger changed the title perf: O(N) array-slice scan + parallel atomic materialisation perf: O(N) array-slice scan + primitive cursor cache Apr 29, 2026
@JohannesLichtenberger JohannesLichtenberger merged commit bf240ac into main Apr 29, 2026
9 checks passed
@JohannesLichtenberger JohannesLichtenberger deleted the perf/array-slice-fixes branch April 29, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant