Skip to content

Specialize non-reducing kernel: drop reduction-only iszero hoist#69

Merged
lkdvos merged 1 commit into
mainfrom
ld-nonreduce-kernel
Jul 10, 2026
Merged

Specialize non-reducing kernel: drop reduction-only iszero hoist#69
lkdvos merged 1 commit into
mainfrom
ld-nonreduce-kernel

Conversation

@lkdvos

@lkdvos lkdvos commented Jun 24, 2026

Copy link
Copy Markdown
Member

The generated inner loop of _mapreduce_kernel! always includes an iszero(stride_1_1) branch that hoists A1[I1] out of the @simd loop. This hoist only matters for reductions, where the destination has stride 0 along the inner dimension. On the non-reducing path (op === nothing: map!, permutedims!, copy!, conj!, ...) every destination element is written exactly once, so the branch is dead code — yet it was still emitted, and can't be eliminated because the condition is not compile time constant, therefore duplicating the inner loop body and leaving a runtime branch in the hot loop.

This PR keys the inner-loop generation on op: the non-reducing case now emits only the unit-stride and generic @simd loops, while the reducing case is unchanged. This is a pure generation-time specialization, so should not affect actual runtimes.

Benchmarks

Compile time of 12 fresh kernel instantiations (map! + permute over various eltypes and ranks), median of 5 alternating trials against main, with the untouched reducing kernels as drift control:

main PR Δ
non-reducing instantiations 5.21 s 4.43 s −15.0%
reducing (control) 2.34 s 2.24 s −4.2% (drift)

Net effect: roughly 10% less compile time for non-reducing kernel instantiations, runtime-neutral.

🤖 Generated with assistance of Claude Code

@lkdvos lkdvos requested a review from Jutho June 24, 2026 01:09
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.37209% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/mapreduce.jl 88.37% 5 Missing ⚠️
Files with missing lines Coverage Δ
src/mapreduce.jl 81.39% <88.37%> (+0.26%) ⬆️

... and 2 files with indirect coverage changes

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

The dim-1 inner core in `_mapreduce_kernel_expr` carried an
`iszero(stride_1_1)` branch that hoists `A1[I1]` out of the `@simd`
loop. That hoist only matters for reductions, where the destination
stride along the inner dim is zero. For the non-reducing path
(`op === nothing`: map!/permute/copy!/fill!/...) every destination
element is written exactly once, so the branch is dead and the loop
body was needlessly generated twice.

Emit the plain `@simd` loop (one body, no runtime branch) for
`op === nothing`, keeping the hoist for reductions. Runtime-neutral
(no permute regression) and trims a bit of non-reducing compile time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lkdvos lkdvos force-pushed the ld-nonreduce-kernel branch from 26b0013 to 13ac5ec Compare July 9, 2026 14:40
Comment thread src/mapreduce.jl
Comment on lines 330 to 341
outerstepstrideex = Vector{Expr}(undef, N)
outerreturnstrideex = Vector{Expr}(undef, N)
for i in 1:N
stepex = Expr(:block)
returnex = Expr(:block)
for j in 1:M
push!(stepex.args, :($(Ivars[j]) += $(blockdimvars[i]) * $(stridevars[i, j])))
push!(returnex.args, :($(Ivars[j]) -= dims[$i] * $(stridevars[i, j])))
end
outerstepstrideex[i] = stepex
outerreturnstrideex[i] = returnex
end

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.

Do we still use these variables? I am a bit rusty on the logic of this generated function.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think so, somewhere in the outer block loop ( although I'm honestly rusty on the logic every time I don't look at it for a while, I have to just generate some of these expressions to really see what is going on... )

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.

Ah yes, the github view conveniently ended before the end of the function, where the outer part blocks started appearing.

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.

Maybe Claude can be asked if this generating function body can be simplified, while still producing the same generated code. The current approach is quite cumbersome.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I tried this multiple times already, with little success...

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

Actual changes look good to me; but I added a question on some other part of the generated function.

@lkdvos lkdvos merged commit ebd6984 into main Jul 10, 2026
12 of 13 checks passed
@lkdvos lkdvos deleted the ld-nonreduce-kernel branch July 10, 2026 00:30
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