Skip to content

Fix type instability with many array references on Julia 1.11+ - #573

Merged
ranocha merged 1 commit into
JuliaSIMD:mainfrom
vchuravy:vc/nested-promote-type
Oct 14, 2026
Merged

ranocha merged 1 commit into
JuliaSIMD:mainfrom
vchuravy:vc/nested-promote-type

Conversation

@vchuravy

Copy link
Copy Markdown
Contributor

determine_eltype emits a single promote_type call with one eltype(array) argument per entry of ls.includedactualarrays, and that has one entry per array reference, not per array. On Julia ≥ 1.11, promote_type with 36 or more arguments is no longer inferred (Base.afoldl has max_args = 34), so the inferred element type is Any. That makes pick_vector_width, MM, __vload, vadd_nsw etc. dynamic, so every SIMD operation in the loop goes through runtime dispatch and allocates.

julia> Core.Compiler.return_type(promote_type, NTuple{35, Type{Float64}})
Type{Float64}

julia> Core.Compiler.return_type(promote_type, NTuple{36, Type{Float64}})
Any        # Julia 1.11.9 and 1.12.6; inferred for all lengths on 1.10

This PR builds a chain of two-argument promote_type calls instead, which is always inferred. The result is the same, since promote_type folds pairwise anyway.

Impact

We hit this in Trixi.jl: a hand-written @turbo flux-differencing kernel for MHD (flux_differencing_kernel_turbo! with nonconservative terms in src/solvers/dgsem_structured/dg_3d_turbo.jl) has about 40 array references per loop. On Julia 1.12 (Ryzen 7 7840U):

before after
one element ~31 ms, 8.6 MB allocated 5.1 µs, 0 bytes
full right-hand side, 512 elements ~18 s 5.5 ms

The results agree with the non-@turbo reference implementation to round-off. The existing sum_way_too_unrolled test in test/manyarrayrefs.jl (272 references) is affected too, but it only checks the result.

Tests

I added a type-stability check to test/manyarrayrefs.jl: sum_way_too_unrolled with 6×7 = 42 references must not allocate. On Julia 1.12.6 it fails on master (68560 bytes allocated) and passes with this change.

Pkg.test() on Julia 1.12.6 has one failure, test/dot.jl:12 (choose_order gives unroll 4 instead of the expected 8 for 32 registers). It is pre-existing: the released v0.12.174 gives the same result on this machine (AVX-512, Zen 4), and the code changed here doesn't affect choose_order. All other test groups pass.

A complementary change would be to deduplicate ls.includedactualarrays per array (e.g. addsetv! instead of push! in reconstruct_loopset.jl, as split_loops.jl already does). That would shorten the generated expression, but on its own it would not fix loops that reference 36+ distinct arrays, so I kept this PR minimal.

This PR was prepared with substantial assistance from Claude Code (Anthropic's AI coding assistant). I reviewed the changes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MEJYUviAUshj5fZt8LTe8A

`determine_eltype` emitted a single `promote_type` call with one argument
per array reference. On Julia >= 1.11, `promote_type` with 36 or more
arguments is not inferred (`Base.afoldl` has `max_args = 34`), so loops with
many array references became type-unstable: the element type and vector
width were `Any`, and every SIMD operation used dynamic dispatch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MEJYUviAUshj5fZt8LTe8A
@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.92%. Comparing base (369cebc) to head (ed42ca9).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #573      +/-   ##
==========================================
+ Coverage   83.81%   86.92%   +3.11%     
==========================================
  Files          40       40              
  Lines        9574     9684     +110     
==========================================
+ Hits         8024     8418     +394     
+ Misses       1550     1266     -284     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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

@ranocha ranocha 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.

Thanks a lot!

@ranocha
ranocha merged commit 09fbe4d into JuliaSIMD:main Oct 14, 2026
63 of 76 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