Skip to content

(feat): setfield!-based N-D wrapper reuse (Julia 1.11+) with 1.10 legacy fallback#20

Merged
mgyoo86 merged 9 commits intomasterfrom
feat/new_array_nd
Mar 6, 2026
Merged

(feat): setfield!-based N-D wrapper reuse (Julia 1.11+) with 1.10 legacy fallback#20
mgyoo86 merged 9 commits intomasterfrom
feat/new_array_nd

Conversation

@mgyoo86
Copy link
Collaborator

@mgyoo86 mgyoo86 commented Mar 6, 2026

Summary

Replace the 4-way set-associative N-D wrapper cache with setfield!-based in-place Array mutation (Julia 1.11+), eliminating the CACHE_WAYS=4 eviction limit. After warmup, any number of dimension patterns per slot is zero-allocation.

Motivation

The old N-way cache (nd_arrays/nd_dims/nd_ptrs/nd_next_way) used round-robin eviction:

  • Limited to CACHE_WAYS (default 4) dimension patterns per slot
  • 5th+ patterns caused repeated unsafe_wrap allocations (~112 bytes each)
  • Required pointer tracking (nd_ptrs) for resize invalidation

Julia 1.11 changed Array to a mutable struct with ref::MemoryRef{T} and size::NTuple{N,Int}, enabling direct field mutation via setfield!.

Changes

Area Change
types.jl Replace 4 cache vectors (nd_arrays/nd_dims/nd_ptrs/nd_next_way) with single nd_wrappers::Vector{Union{Nothing, Vector{Any}}} indexed by N (dimensionality)
acquire.jl Rewrite get_nd_array!setfield!(:ref, ...) + setfield!(:size, dims) for 0-alloc reuse; add _store_nd_wrapper! helper
bitarray.jl Rewrite get_bitarray!setfield!(:len/:dims/:chunks) for 0-alloc BitArray reuse
state.jl Simplify empty! (4 cache clears → 1)
AdaptiveArrayPools.jl Single @static if VERSION >= v"1.11-" gate for all includes
src/legacy/ Full copy of pre-1.11 code (types/acquire/bitarray/state) for Julia 1.10 compat
test/ Version-gated test runner; new test/legacy/test_nway_cache.jl; updated test_nway_cache.jl with 10+ pattern zero-alloc tests

Performance

Scenario Before (4-way cache) After (setfield!)
4 dim patterns / slot 0 alloc 0 alloc
5 dim patterns / slot ~112 B/call (eviction) 0 alloc
10+ dim patterns / slot ~112 B/call (eviction) 0 alloc
Cache lookup ~5-10 ns (Dict hash) ~1 ns (Vector index by N)

Compatibility

  • Julia 1.11+: New setfield! path (unlimited patterns, 0-alloc)
  • Julia 1.10: Legacy fallback with original 4-way cache (src/legacy/)

mgyoo86 added 4 commits March 4, 2026 20:23
Replace 4-way set-associative cache with Dict{Int,Vector{Any}}-keyed
wrapper cache that uses setfield!(:size) and setfield!(:ref) for
zero-allocation Array reuse. Eliminates CACHE_WAYS=4 eviction limit —
unlimited dimension patterns per slot after warmup.

- types.jl: nd_arrays/nd_dims/nd_ptrs/nd_next_way → nd_wrappers Dict
- acquire.jl: rewrite get_nd_array! with setfield! + _store_nd_wrapper!
- bitarray.jl: rewrite get_bitarray! with setfield!(:len/:dims/:chunks)
- state.jl: simplify empty! (4 calls → 1)
- Bump julia compat 1.10 → 1.11, version 0.2.1 → 0.3.0

Fix: always update MemoryRef (no pointer guard) — resize! can grow
vectors in-place without changing pointer, staling cached Memory length.
Extend compat range from Julia 1.11 down to 1.10 by introducing
version-gated includes in AdaptiveArrayPools.jl. Julia 1.11+ uses the
setfield!-based N-D wrapper reuse path; Julia 1.10 falls back to
src/legacy/ (N-way eviction cache). Version bumped to 0.2.2.

- Add @static VERSION guards in main module for types/acquire/bitarray/state
- Add src/legacy/{types,acquire,bitarray,state}.jl for Julia 1.10
- Gate 5-way and 10+-pattern allocation tests by VERSION
- Exclude src/legacy/ from codecov reporting
- Trim verbose docstrings for clarity
Replace Dict{Int,Vector{Any}} (hash-based, ~5-10ns per lookup) with
Vector{Union{Nothing,Vector{Any}}} indexed directly by N (dimensionality,
~1ns per lookup) for the nd_wrappers cache in TypedPool and BitTypedPool.

Also fix _store_nd_wrapper! to use resize! instead of while+push! loop
for growing the per-slot wrapper vector.
@codecov
Copy link

codecov bot commented Mar 6, 2026

Codecov Report

❌ Patch coverage is 95.81056% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.38%. Comparing base (32d7e44) to head (11b8536).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/legacy/state.jl 94.44% 13 Missing ⚠️
src/legacy/acquire.jl 94.55% 8 Missing ⚠️
src/acquire.jl 96.87% 1 Missing ⚠️
src/legacy/types.jl 97.91% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #20      +/-   ##
==========================================
- Coverage   96.47%   95.38%   -1.09%     
==========================================
  Files           9       13       +4     
  Lines        1276     1756     +480     
==========================================
+ Hits         1231     1675     +444     
- Misses         45       81      +36     
Files with missing lines Coverage Δ
src/AdaptiveArrayPools.jl 100.00% <ø> (ø)
src/bitarray.jl 92.59% <100.00%> (-7.41%) ⬇️
src/legacy/bitarray.jl 100.00% <100.00%> (ø)
src/state.jl 98.24% <100.00%> (-0.48%) ⬇️
src/types.jl 95.83% <ø> (-4.17%) ⬇️
src/acquire.jl 90.66% <96.87%> (-4.58%) ⬇️
src/legacy/types.jl 97.91% <97.91%> (ø)
src/legacy/acquire.jl 94.55% <94.55%> (ø)
src/legacy/state.jl 94.44% <94.44%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

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

This PR modernizes N-D wrapper reuse by switching the CPU unsafe_acquire!/BitArray paths to setfield!-based in-place wrapper mutation on Julia 1.11+, while preserving Julia 1.10 behavior via a src/legacy/ fallback. It also updates tests and documentation to reflect the new caching model (unlimited dimension patterns per slot after warmup).

Changes:

  • Replace the CPU N-way set-associative N-D wrapper cache with nd_wrappers + setfield! reuse (Julia 1.11+), with src/legacy/* providing the prior 1.10 implementation.
  • Update BitArray wrapper reuse to use setfield!-based mutation (Julia 1.11+).
  • Add/version-gate tests and refresh docs for the new caching behavior and CACHE_WAYS scope.

Reviewed changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/AdaptiveArrayPools.jl Version-gates includes to switch between 1.11+ and legacy 1.10 implementations.
src/types.jl Replaces old N-way cache fields with nd_wrappers in pool types and updates docstrings.
src/acquire.jl Implements _store_nd_wrapper! and get_nd_array! wrapper reuse via setfield! (1.11+).
src/bitarray.jl Implements BitArray wrapper reuse via nd_wrappers + setfield! updates (1.11+).
src/state.jl Simplifies empty! to clear nd_wrappers instead of multiple N-way vectors.
src/legacy/types.jl Adds legacy 1.10 types/constants (including N-way cache + CACHE_WAYS).
src/legacy/acquire.jl Adds legacy 1.10 acquire path with N-way cache logic.
src/legacy/bitarray.jl Adds legacy 1.10 BitArray acquisition with N-way caching.
src/legacy/state.jl Adds legacy 1.10 state management with empty! clearing N-way cache fields.
test/runtests.jl Version-gates which tests/helpers to include (1.11+ vs legacy).
test/test_state.jl Switches N-D cache preservation assertion to _test_nd_cache_preserved.
test/test_nway_cache.jl Updates allocation expectations for 1.11+ and adds nd_wrappers-specific tests.
test/legacy/test_nway_cache.jl Introduces legacy-only tests that preserve N-way eviction/allocation expectations.
docs/src/reference/api.md Clarifies set_cache_ways! relevance (1.10/CUDA; no effect on 1.11+ CPU).
docs/src/features/cuda-support.md Updates CUDA docs to distinguish CPU 1.11+ behavior vs CUDA N-way cache.
docs/src/features/configuration.md Updates CACHE_WAYS documentation scope (1.10/CUDA).
docs/src/features/bit-arrays.md Updates BitArray caching description for 1.11+ vs 1.10/CUDA.
docs/src/basics/api-essentials.md Updates unsafe_acquire! allocation behavior by Julia version.
docs/src/architecture/type-dispatch.md Documents new nd_wrappers approach + legacy N-way path.
docs/src/architecture/how-it-works.md Documents wrapper reuse on 1.11+ and legacy/cuda behavior.
docs/src/architecture/design-docs.md Notes N-way cache is now legacy on CPU 1.11+.
docs/design/nd_array_approach_comparison.md Adds update notice re: setfield! wrapper reuse on 1.11+ CPU.
docs/design/hybrid_api_design.md Adds update notice re: setfield! wrapper reuse and field changes.
docs/design/cuda_extension_design.md Adds update notice clarifying CUDA still uses N-way cache.
Project.toml Reorders/normalizes some entries without changing the version value.

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

mgyoo86 added 5 commits March 5, 2026 19:51
- src/AdaptiveArrayPools.jl: merge 3 @static if blocks into one
- test/runtests.jl: single @static if with version-specific helper
- test/test_nway_cache.jl: remove @static, keep v1.11+ only
- test/test_state.jl: replace @static with _test_nd_cache_preserved helper
- test/legacy/test_nway_cache.jl: legacy N-way cache tests (≤1.10)
- src/acquire.jl: _store_nd_wrapper! while→resize! optimization
- src/state.jl: remove redundant inline type comments
Update all documentation to reflect that Julia 1.11+ CPU uses setfield!-based
wrapper reuse (0-alloc, unlimited dimension patterns) instead of N-way cache.
Clarify that CACHE_WAYS and N-way cache are now only relevant for CUDA backend
and Julia 1.10 legacy path.
- Fix setfield! docstring signatures to use correct 3-arg form
- Move _test_nd_cache_preserved helper before ARGS branch so it's
  defined when running individual test files
- Add 10th dimension pattern to "10+ patterns" test to match name
- Exclude src/legacy/ from codecov (0% on Julia 1.11+ by design)
@mgyoo86 mgyoo86 force-pushed the feat/new_array_nd branch from c4cba3c to 11b8536 Compare March 6, 2026 03:52
@mgyoo86 mgyoo86 merged commit 2adde8d into master Mar 6, 2026
10 checks passed
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