Skip to content

Fix spurious allocations in ret_allocs test helper on Julia 1.10/1.11#227

Merged
ChrisRackauckas merged 1 commit into
JuliaDiff:masterfrom
ChrisRackauckas-Claude:fix-alloc-test-helper
Jul 12, 2026
Merged

Fix spurious allocations in ret_allocs test helper on Julia 1.10/1.11#227
ChrisRackauckas merged 1 commit into
JuliaDiff:masterfrom
ChrisRackauckas-Claude:fix-alloc-test-helper

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown

Summary

The "Non-allocating cache construction" testset (test/finitedifftests.jl) currently fails on Julia 1.10 and 1.11 with 12 Evaluated: 48 == 0 failures at :371/:379. It passes on Julia 1.12. This is a test-helper artifact, not a library regression — the finite_difference_gradient! path is genuinely allocation-free.

Root cause

function ret_allocs(res, _f, x, cache)
    @allocated FiniteDiff.finite_difference_gradient!(res, _f, x, cache)
end

Julia's default heuristic does not specialize a method on a Function-typed argument that is only forwarded to an inner call. So _f is boxed inside ret_allocs, the inner finite_difference_gradient! becomes a dynamic dispatch, and the wrapper itself allocates ~48 bytes on 1.10/1.11 (0 on 1.12+, where the heuristic improved). The library work is 0 bytes.

Independently verified the library allocates nothing:

  • @allocated inside a function around the call = 0
  • 1000 direct calls measured via Base.gc_bytes() = 0 total
  • Profile.Allocs on a normally-compiled call = 0 allocations

Fix

Force specialization on the function argument:

function ret_allocs(res, _f::F, x, cache) where {F}
    @allocated FiniteDiff.finite_difference_gradient!(res, _f, x, cache)
end

Verification

Ran the full Core suite locally on Julia 1.11: FiniteDiff Standard Tests | 219 pass, 0 fail, 1 brokenTesting FiniteDiff tests passed (previously 207 pass / 12 fail / 1 broken).

Please ignore until reviewed by @ChrisRackauckas.

🤖 Generated with Claude Code

The "Non-allocating cache construction" testset asserts
ret_allocs(...) == 0, but the helper did not specialize on its `_f`
Function argument. Julia's default heuristic skips specialization for a
Function-typed argument that is only forwarded, so the inner
finite_difference_gradient! call dispatched dynamically and the wrapper
itself allocated ~48 bytes on Julia 1.10 and 1.11 (0 on 1.12+, where the
heuristic improved). This produced 12 `Evaluated: 48 == 0` failures on
the LTS while the library path is genuinely allocation-free (verified 0
bytes over 1000 direct calls).

Force specialization with `_f::F where {F}`; the full Core suite then
passes on Julia 1.11 (219 pass, 0 fail, 1 broken).

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas
ChrisRackauckas marked this pull request as ready for review July 12, 2026 07:23
@ChrisRackauckas
ChrisRackauckas merged commit 8b347cc into JuliaDiff:master Jul 12, 2026
5 of 6 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