Skip to content

fix(compute/exec): do not carry a cached null count across ArraySpan.SetSlice - #1306

Merged
zeroshade merged 2 commits into
apache:mainfrom
singhpratech:fix-exec-setslice-null-count
Oct 13, 2026
Merged

zeroshade merged 2 commits into
apache:mainfrom
singhpratech:fix-exec-setslice-null-count

Conversation

@singhpratech

Copy link
Copy Markdown
Contributor

Rationale for this change

and_kleene and or_kleene return false where the answer is null when ExecCtx.ChunkSize is
below the input length (#1305). ArraySpan.SetSlice kept the cached null count for the new
slice when it was 0 or equal to the old length. That is only right while the count describes the
whole span. The executor reuses one span per argument across slices, and a kernel that calls
UpdateNullCount() on it stores the slice's count, so the next SetSlice treats the following
slice as all valid or all null.

What changes are included in this PR?

SetSlice resets the count to UnknownNullCount whenever a validity bitmap is present, as the C++
ArraySpan::SetSlice does (cpp/src/arrow/array/data.h), and keeps it unknown when it was already
non-zero without a bitmap. The null type still gets the slice length; a span with no bitmap and a
zero count keeps zero. Two cases added to TestArraySpan_SetSlice, and a new test that runs both
Kleene kernels at chunk sizes 1, 2, 3, 4 and 8 against the default result.

Cost: a slice of an all-valid array with a bitmap now reports an unknown count, so a kernel that
asks pays one popcount over the slice instead of reading the cache. That is the C++ behaviour.

Are these changes tested?

Yes. The new chunk-size test fails on main (four assertions, chunk sizes 1 and 2 for both kernels)
and passes with the change; go test ./arrow/compute/... passes.

Are there any user-facing changes?

Results of and_kleene/or_kleene no longer depend on ExecCtx.ChunkSize. ArraySpan.Nulls after
SetSlice is UnknownNullCount in cases where it was previously 0 or the slice length; code that
read it directly should call UpdateNullCount(), which is what the package's own kernels do.

Closes #1305.

…SetSlice

SetSlice kept the cached null count for the new slice when it was 0 or
equal to the old length. That is only right while the count describes
the whole span; a kernel that calls UpdateNullCount on the span the
executor reuses stores the slice's count, and the next SetSlice then
treats the following slice as all valid or all null. and_kleene and
or_kleene return false where the answer is null once ExecCtx.ChunkSize
is below the input length. Reset the count to unknown whenever a
validity bitmap is present, as the C++ ArraySpan::SetSlice does, and
add the chunk-size test.

@zeroshade zeroshade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The null-count invalidation fix and focused chunk-size tests pass. One required release-audit fix remains: the new test file needs the standard ASF header.

@singhpratech

Copy link
Copy Markdown
Contributor Author

Added the standard ASF header to arrow/compute/scalar_boolean_chunksize_test.go in the
latest commit. Thanks for the review.

singhpratech added a commit to singhpratech/ArrowMetal that referenced this pull request Sep 15, 2026

@zeroshade zeroshade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the SetSlice invalidation fixes the reproduced chunk-size-dependent Kleene results for reused spans (matching the C++ ArraySpan::SetSlice behavior), and the previously requested ASF license header is now present.

Bitmap-counting trade-off (arrow/compute/exec/span.go:222)

Non-blocking observation: the conservative invalidation is correct for both cached zero and cached all-null counts. Those counts describe the previous active slice, not necessarily the next one. Preserving zero is safe when the requested range is known to belong to an all-valid parent, but ArraySpan currently stores no separate parent-range/null-count provenance. Simply retaining Nulls == 0 or Nulls == Len would reintroduce the bug. The executor still determines null-free output-bitmap elision from the original datums in setupPrealloc, independently of this change.

There is a measurable cost. In short local benchmarks with 1,048,576 all-valid values and allocated validity bitmaps, median and_kleene time went from 127.6 to 155.7 µs at ChunkSize=1024, and from 49.9 to 66.3 µs at ChunkSize=32768; corresponding or_kleene medians increased approximately 19% and 28%. Default-size Boolean runs differed by about −2% to +3%, and sliced Int64 add by +2% to +4%; allocation counts were unchanged. These were three 200 ms samples per case, not statistically isolated performance estimates. The extra work includes recounting sliced input bitmaps and recounting the final expanded output bitmap. A retained chunk-size benchmark would help guard this trade-off, but I would not preserve the unsafe cache to avoid it.

Smaller observations

  • The new tests genuinely reproduce the defect: with the baseline/main span.go substituted into the PR test tree, both added SetSlice cases fail and both Kleene kernels fail at chunk sizes 1 and 2. All pass at this head, and go test ./arrow/compute/... -count=1 passes.
  • Additional local probes passed for cached-zero → all-null → valid transitions, nonzero offsets, identical slices, empty slices, bitmap-free spans, and the NULL type. NULL still receives the slice length; bitmap-free known-zero spans retain zero. SetSlice remains a serial mutation of offset/length/count; UpdateNullCount's atomics do not make concurrent re-slicing safe.
  • CI has 28 successful checks, one skipped upload, and one failure: AMD64 Windows MinGW 64 CGO. Its wcstod/wcstof redefinition errors match the unrelated main-branch toolchain problem fixed by PR #1369, not this change.

Prior review points

  • ✅ Add the standard ASF header to the new test file rather than excluding it from RAT — addressed. This was the sole requested change in zeroshade's prior review and inline thread. Commit 0bc3fea955 adds the header at arrow/compute/scalar_boolean_chunksize_test.go:1–15; no RAT exclusion was added. Both RC verification jobs now succeed.

This review was drafted by an AI-assisted tool and
confirmed by an Apache Arrow maintainer. The maintainer
approving this PR has read the findings and signed off. If
something feels off, please reply on the PR and a maintainer
will follow up.

More on how Apache Arrow handles maintainer review:
CONTRIBUTING.md.

@zeroshade
zeroshade merged commit 7090f9e into apache:main Oct 13, 2026
29 of 30 checks passed
singhpratech added a commit to singhpratech/ArrowMetal that referenced this pull request Oct 15, 2026
…null-count fix, commit 7090f9e; #1305 closed with it), named with the other merged Go work; apache/arrow#51222 has a contributor's fix pull request #52451 under review
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants