Skip to content

Conversation

@ChrisRackauckas-Claude
Copy link

Summary

  • Fix type instabilities in gradient/hessian closures that caused Core.Box captures
  • Replace incorrect value_derivative_and_second_derivative! with value_gradient_and_hessian!
  • Achieves 2.2x speedup and eliminates all allocations in gradient hot path

Problem

The closures generated by instantiate_function were capturing prep_grad, prep_hess, etc. with Core.Box types due to conditional assignments inside if-blocks. This caused:

  • Type instability in hot path functions (visible in @code_warntype)
  • Extra allocations on every gradient/hessian call
  • ~2x slower performance compared to direct DifferentiationInterface calls

Solution

Use let blocks to capture preparation objects with concrete types when creating closures. This is a well-known Julia performance pattern to avoid Core.Box boxing.

Benchmark Results

Gradient evaluation (ForwardDiff, 2D Rosenbrock):

Metric Before After Improvement
Time ~80 ns ~36 ns 2.2x faster
Allocations 1 (16 bytes) 0 Eliminated
Overhead vs DI 124% <1% 99% reduction

Additional Fix

Fixed incorrect use of value_derivative_and_second_derivative! (for scalar functions) in the fgh! closure. This function is for scalar→scalar differentiation. For optimization (vector→scalar), the correct function is value_gradient_and_hessian!.

Test Plan

  • Basic functionality tests pass (grad, fg, hess, hv, fgh)
  • @code_warntype shows no type instabilities
  • Benchmark confirms performance improvement

cc @ChrisRackauckas

🤖 Generated with Claude Code

This PR fixes a significant performance issue in the gradient, hessian, and
other derivative closures generated by `instantiate_function` when using
DifferentiationInterface (DI) AD backends.

## Problem

The closures were capturing `prep_grad`, `prep_hess`, etc. variables with
`Core.Box` types due to conditional assignments inside if-blocks. This caused:
- Type instability in the hot path functions
- Extra allocations on every gradient/hessian call
- ~2x slower performance than direct DI calls

## Solution

Use `let` blocks to capture preparation objects with concrete types when
creating closures. This ensures the closures are type-stable and allocation-free.

## Performance Improvement

Gradient evaluation (ForwardDiff, 2D Rosenbrock):
- Before: ~80 ns, 1 allocation (16 bytes), 124% overhead vs DI
- After:  ~36 ns, 0 allocations, <1% overhead vs DI

This is a **2.2x speedup** and **eliminates all allocations** in the
gradient evaluation hot path.

## Additional Fix

Fixed incorrect use of `value_derivative_and_second_derivative!` (for scalar
functions) in fgh! closure. Replaced with correct `value_gradient_and_hessian!`
(for multivariate optimization functions).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
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.

3 participants