exploit symmetry in the hessian - #837
Conversation
instead of relying on the jacobian of gradient for the hessian explicitly seed dual numbers and only calculate the upper triangular part when chunking, gives ~2x speedup as input length becomes big
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #837 +/- ##
==========================================
+ Coverage 90.68% 91.30% +0.61%
==========================================
Files 11 11
Lines 1052 1115 +63
==========================================
+ Hits 954 1018 +64
+ Misses 98 97 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Thanks, this is a nice improvement. I went through it with the help of Claude. The core of it holds up. I reproduced the speedup locally with rosenbrock and the default chunk size: 49.3 -> 31.8 µs at The failing A few comments. The single-chunk case became slowerSince there is no vector-mode path anymore, seed_hessian_chunk!(xdual, x, 1, nothing, nothing, xlen) # zeroes everything
seed_hessian_chunk!(xdual, x, 1, iseeds, oseeds) # ... then overwrites block 1
seed_hessian_chunk!(xdual, x, 1, iseeds, oseeds)
seed_hessian_chunk!(xdual, x, N + 1, nothing, nothing, xlen - N)
...
nblocks > 1 && seed_hessian_chunk!(xdual, x, 1, nothing, nothing)turns a ~6% regression into a ~12-16% improvement over master, and gives identical results for every chunk size I tried:
It matters more than it looks, because the buffer is While you are in there: The StaticArrays path is left behind
So after this PR function ForwardDiff.hessian(f::F, x::StaticArray) where {F}
T = typeof(Tag(f, eltype(x)))
return extract_hessian(T, partials(T, f(dualize(T, dualize(T, x)))), x)
endplus
Structured inputs silently change shapeFor julia> size(ForwardDiff.hessian(z -> sum(abs2, z), UpperTriangular(rand(3, 3))))
(9, 6) # master
(6, 6) # this PRThe new shape is the right one (master mixes linear and structural indices, which I would call a bug), and I checked that the values are correct. But we have no Hessian test for structured inputs at all, so this is completely untested, and it is breaking for Docs
Smaller things
CoverageThe
Unrelated to the coverage numbers, but while adding tests: |
|
Addressed the review feedback in Highlights:
Benchmarks compare
Memory and allocation counts were unchanged. The complete test suite passes: 9,396/9,396 tests. |
Instead of relying on the Jacobian of gradient for the Hessian, explicitly seed dual numbers and only calculate the upper triangular part when chunking, gives ~2x speedup as input length becomes big
Fixes #836 cc @gdalle
Basically one-shotted by Claude and then decringified with gpt.