Skip to content

(fix): Use legacy N-way cache path on Julia 1.11#41

Merged
mgyoo86 merged 4 commits intomasterfrom
fix/v1.11_to_legacy
Mar 31, 2026
Merged

(fix): Use legacy N-way cache path on Julia 1.11#41
mgyoo86 merged 4 commits intomasterfrom
fix/v1.11_to_legacy

Conversation

@mgyoo86
Copy link
Copy Markdown
Member

@mgyoo86 mgyoo86 commented Mar 31, 2026

Problem

On Julia 1.11, length(::Array) is computed from the backing Memory object's length (arraylen builtin), not from prod(size(arr)). This was fixed in Julia 1.12.

The setfield!(:ref)-based wrapper reuse in get_array! copies the entire MemoryRef from the backing vector. Since pool slots grow but never shrink, the backing vector can be larger than prod(dims):

# Julia 1.11
v = acquire!(pool, Float64, 10, 10)   # backing vec sized to 100
rewind!(pool, Float64)
b = acquire!(pool, Float64, 5, 4)     # same slot, backing vec still 100
length(b)   # 100 ← WRONG (should be 20)
vec(b)      # size (100,) ← breaks downstream code

This caused DimensionMismatch errors in production.

Fix

Move the modern/legacy version gate from v"1.11-" to v"1.12-":

  • Julia ≤1.11: Uses the legacy N-way set-associative cache path (unsafe_wrap + cache). Zero-alloc on cache hit (up to CACHE_WAYS patterns per slot, default 4).
  • Julia 1.12+: Uses the modern setfield!(:ref, :size) path. Zero-alloc unconditionally (unlimited dimension patterns).

GPU extensions (CUDA, Metal) are also gated at v"1.12-" because they import modern-path-only functions (_store_arr_wrapper!) from the base package.

Impact

Julia version CPU path GPU pooling Zero-alloc?
≤1.10 legacy (N-way cache) disabled cache hit: yes
1.11 legacy (N-way cache) disabled cache hit: yes
1.12+ modern (setfield!) enabled always

mgyoo86 added 2 commits March 31, 2026 14:22
Julia 1.11's arraylen builtin derives length from Memory.length, not
prod(size). This causes setfield!(:ref)-based wrapper reuse to return
arrays where length(arr) != prod(size(arr)), breaking vec(), reshape(),
and downstream code.

Move the modern/legacy version gate from v"1.11-" to v"1.12-" so that
Julia 1.11 uses the legacy N-way set-associative cache path (which uses
unsafe_wrap and computes length correctly). Julia 1.12+ continues to use
the zero-alloc setfield! path where the bug is fixed.

GPU extensions (CUDA, Metal) are also gated at v"1.12-" because they
import modern-path-only functions (_store_arr_wrapper!) from the base
package.
All references to "Julia 1.11+" in comments, docstrings, and section
headers now say "Julia 1.12+" to match the version gate change.

Left unchanged: factual statements about Julia 1.11 behavior (e.g.
"FieldError is 1.11+", "Julia 1.11's arraylen uses Memory.length").
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.48%. Comparing base (3118fd0) to head (a2f444c).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #41      +/-   ##
==========================================
- Coverage   96.03%   95.48%   -0.56%     
==========================================
  Files          14       14              
  Lines        3232     3232              
==========================================
- Hits         3104     3086      -18     
- Misses        128      146      +18     
Files with missing lines Coverage Δ
src/AdaptiveArrayPools.jl 100.00% <ø> (ø)
src/acquire.jl 90.67% <ø> (-5.70%) ⬇️
src/bitarray.jl 88.88% <ø> (-2.23%) ⬇️
src/convenience.jl 98.70% <ø> (ø)
src/debug.jl 95.00% <ø> (ø)
src/legacy/state.jl 96.27% <ø> (ø)
src/types.jl 85.52% <ø> (-3.95%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adjusts version gating so Julia 1.11 uses the legacy N-way cache path (avoiding incorrect length(::Array)/vec behavior), while keeping the modern setfield! wrapper-reuse path and GPU extensions enabled only on Julia 1.12+.

Changes:

  • Moved the modern/legacy version gate from v"1.11-" to v"1.12-" in the package entrypoint and related code paths.
  • Updated tests to only exercise setfield!-based invalidation/zero-allocation behavior on Julia 1.12+ and use legacy expectations on ≤1.11.
  • Gated CUDA/Metal extensions and their tests to Julia 1.12+ and updated related documentation/comments.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/AdaptiveArrayPools.jl Switches the include/version gate to VERSION >= v"1.12-" so Julia 1.11 loads legacy implementation.
src/acquire.jl Updates documentation/comments for setfield!-based reshape/acquire path to be 1.12+.
src/bitarray.jl Updates header/comments to reflect BitArray setfield-based reuse is for Julia 1.12+.
src/convenience.jl Updates reshape! doc wording to 1.12+ path.
src/debug.jl Gates wrapper-mutation checks to 1.12+ and updates related assumptions/comments.
src/types.jl Updates type docs to describe arr_wrappers cache as 1.12+ feature.
src/legacy/state.jl Updates legacy header/docs to indicate legacy applies through Julia ≤1.11.
ext/AdaptiveArrayPoolsCUDAExt/AdaptiveArrayPoolsCUDAExt.jl Requires Julia 1.12+ for CUDA pooling and updates warning text/comments.
ext/AdaptiveArrayPoolsCUDAExt/types.jl Updates CUDA pool type docs to match Julia 1.12+ CPU design reference.
ext/AdaptiveArrayPoolsCUDAExt/acquire.jl Updates comments to reference CPU’s Julia 1.12+ approach.
ext/AdaptiveArrayPoolsMetalExt/AdaptiveArrayPoolsMetalExt.jl Requires Julia 1.12+ for Metal pooling and updates warning text/comments.
ext/AdaptiveArrayPoolsMetalExt/types.jl Updates Metal pool type docs to match Julia 1.12+ CPU design reference.
test/runtests.jl Updates version-based test selection and helpers to gate modern-path tests on 1.12+.
test/test_zero_allocation.jl Updates per-iteration allocation tolerance comment/version gate to 1.12+.
test/test_safety.jl Gates setfield!-based invalidation assertions to run only on Julia 1.12+.
test/test_reshape.jl Gates “zero allocation reshape” tests to Julia 1.12+.
test/test_nway_cache.jl Updates comments describing unlimited-pattern zero-alloc behavior to Julia 1.12+.
test/cuda/runtests.jl Skips CUDA extension tests on Julia < 1.12 and updates messaging.
test/metal/runtests.jl Skips Metal extension tests on Julia < 1.12 and updates messaging.
Comments suppressed due to low confidence (1)

src/convenience.jl:352

  • This docstring still says “On Julia 1.10 and CUDA, falls back to Base.reshape”, but after moving the version gate to 1.12 the fallback behavior also applies on Julia 1.11 (since the setfield!-based reshape implementation isn’t included there). Please update the version wording here so it matches the new gating.
On Julia 1.12+:
- If `ndims(A) == length(dims)` (same dimensionality), `reshape!` mutates `A`
  in-place by changing its size. This differs from `Base.reshape`, which always
  returns a new wrapper.
- For cross-dimensional reshapes (`ndims(A) != length(dims)`), the returned
  `Array` wrapper is taken from the pool's internal cache and may be reused
  after `rewind!` or pool scope exit.

As with all pool-backed objects, the reshaped result must not escape the
surrounding `@with_pool` scope.

On Julia 1.10 and CUDA, falls back to `Base.reshape`.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

mgyoo86 added 2 commits March 31, 2026 14:34
- src/acquire.jl: clarify Array became mutable struct in 1.11 but
  setfield! reuse is only safe from 1.12 due to arraylen bug
- src/debug.jl: legacy fallback covers ≤1.11 (not just 1.10)
- src/legacy/state.jl: fix typo nd_wrappers → arr_wrappers
- src/convenience.jl: reshape! fallback "Julia 1.10" → "Julia ≤1.11"
- README.md: CUDA/Metal "1.11+" → "1.12+", reshape table updated
Update all documentation under docs/src/ to reflect the version gate
change. Historical facts preserved (Julia 1.11 made Array a mutable
struct) with added notes about the arraylen bug that makes the
setfield! path unsafe on 1.11.
@mgyoo86 mgyoo86 merged commit 7018183 into master Mar 31, 2026
13 of 14 checks passed
@mgyoo86 mgyoo86 deleted the fix/v1.11_to_legacy branch March 31, 2026 21:44
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.

2 participants