Repository navigation
Conversation
Constructing the step range `1:blocks[i]:dims[i]` computes its last element with an integer division, which is paid for every block loop in the nest, also when the arrays fit in a single block. A while loop only needs comparisons and additions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nels Whether the innermost dimension has unit stride in all arrays is loop invariant, but it was checked every time the innermost loop started. For arrays with short innermost dimensions this is a large part of the work. Non-reducing kernels now generate the loop nest for both cases and select one up front. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 29, 2026
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR reduces the fixed cost of the mapreduce kernel, which dominates for small arrays such as the blocks of a block-sparse tensor.
for J in 1:blocks[i]:dims[i]constructs a step range for every block loop, which computes its last element with an integer division. A while loop only needs comparisons and additions.Neither change affects first-call compile time noticeably.
Benchmarks
C = α * permutedims(A, (1, 4, 2, 5, 3, 6))through_mapreducedim!, as TensorOperations calls it, withβ = 0, single-threaded, time in ns:The largest case is within the noise of the machine. The reducing kernel that is used for
β ≠ 0only gains from the first change, a few percent.First calls in a fresh session (a 5-d permutation, a broadcast, a reduction and a
map!): 2.27 s on this branch against 2.35 s on main.Large arrays
To check for regressions on large arrays, this branch and main were loaded side by side in one process and sampled alternately, which keeps load on the machine from biasing the comparison. Median time of this PR relative to main, single-threaded, Intel Xeon Gold 6244 (AVX-512):
(3, 1, 4, 2), β = 0(5, 3, 1, 4, 2), β = 0(4, 3, 2, 1), β = 0map!over 3 contiguous arrays, 2²² elementsmap!over 3 stride-2 arrays, 1024 × 4096sumover dims(1, 3)and2, 256 × 256 × 64The
whileloops are neutral here; the differences come from the hoisted unit-stride check. With the check inside the loop nest, LLVM versions the strided inner loop for a destination with unit stride. Without it, the strided nest has no such version:Hoisting a third, destination-unit-stride variant fixes the Float64 16⁵ case (1.00), but also brings back the slow ComplexF64 loops (0.91–1.15 relative to main), so this PR does not do that. The balance may differ on AVX2 machines, which have no scatter instructions.
With 8 threads the measurements are noisier; the minimum times are within ±5% of main except for the same ComplexF64 improvements.
The tests pass, single- and multi-threaded (4 threads), including the JLArray and Aqua tests.
🤖 Generated with Claude Code