Skip to content

Inline dual_definition_retval to fix nested Dual performance#825

Open
KristofferC wants to merge 1 commit into
masterfrom
kc/inline-dual-definition-retval
Open

Inline dual_definition_retval to fix nested Dual performance#825
KristofferC wants to merge 1 commit into
masterfrom
kc/inline-dual-definition-retval

Conversation

@KristofferC

Copy link
Copy Markdown
Collaborator

👨 I am generating some benchmarks for my JuliaCon talk for https://github.com/KristofferC/HyperHessians.jl, and I wanted to drill down into why HyperHessians was faster than ForwardDiff. One of the reasons was a failure to inline, which just seemed like a mistake, and I don't want to base conclusions of the presentation on performance bugs in other libraries. With this PR the performance of hessian is greatly improved.


🤖

dual_definition_retval carries no @inline annotation. For plain Duals the inliner picks it up anyway, but for nested Duals (as used by hessian) the struct is large enough that Julia's inlining cost model declines it. Every DiffRules-generated operation -- including * and / -- then compiles to a scalar op plus out-of-line calls that return the full Dual through an sret memcpy (648 bytes for Dual{T,Dual{T,Float64,8},8}, per call, per op).

Forcing the inline turns e.g. the nested-Dual multiply into straight-line inlined code. Measured on
Dual{Nothing,Dual{Nothing,Float64,8},8} vectors (256 elements, ns per element):

before after
* (M4) 65.4 22.7
* (Zen 4) 127.6 53.5
sqrt (M4) 34.0 14.7
sqrt (Zen 4) 81.1 13.0

End-to-end ForwardDiff.hessian! at n=256, chunk 8: 28.3 -> 20.4 ms (ackley) and 41.2 -> 23.9 ms (rosenbrock) on an Apple M4; 46.3 -> 25.7 ms and 56.7 -> 34.0 ms on an AMD EPYC 9354. First-order (non-nested) Duals already inlined before this change and are unaffected.

`dual_definition_retval` carries no `@inline` annotation. For plain
`Dual`s the inliner picks it up anyway, but for nested `Dual`s (as used
by `hessian`) the struct is large enough that Julia's inlining cost
model declines it. Every DiffRules-generated operation -- including `*`
and `/` -- then compiles to a scalar op plus out-of-line calls that
return the full `Dual` through an `sret` memcpy (648 bytes for
`Dual{T,Dual{T,Float64,8},8}`, per call, per op).

Forcing the inline turns e.g. the nested-`Dual` multiply into
straight-line inlined code. Measured on
`Dual{Nothing,Dual{Nothing,Float64,8},8}` vectors (256 elements, ns per
element):

|              | before | after |
|--------------|--------|-------|
| `*` (M4)     |  65.4  | 22.7  |
| `*` (Zen 4)  | 127.6  | 53.5  |
| `sqrt` (M4)  |  34.0  | 14.7  |
| `sqrt` (Zen 4) | 81.1 | 13.0  |

End-to-end `ForwardDiff.hessian!` at n=256, chunk 8: 28.3 -> 20.4 ms
(ackley) and 41.2 -> 23.9 ms (rosenbrock) on an Apple M4; 46.3 -> 25.7 ms
and 56.7 -> 34.0 ms on an AMD EPYC 9354. First-order (non-nested) `Dual`s
already inlined before this change and are unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.74%. Comparing base (090ddbb) to head (24a30d6).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #825   +/-   ##
=======================================
  Coverage   90.74%   90.74%           
=======================================
  Files          11       11           
  Lines        1070     1070           
=======================================
  Hits          971      971           
  Misses         99       99           

☔ 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.

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.

1 participant