Depends on how the Jacobian structure is exploited. When both using brute force(or in general, exploit the jacobian’s structure to a same degree) the theory says AD should be faster. But when the matrix structure is put consideration brute force AD may not be. Demo below.

Finite difference with banded jacobian.

```
module func
use fazang
implicit none
contains
function f(x, xl, xr) result(fx)
real(rk), intent(in) :: x, xl, xr
real(rk) :: fx
fx = x*x + xl*xl + xr*xr
end function f
function jac(x, n) result(a)
integer, intent(in) :: n
real(rk), intent(in) :: x(n)
real(rk) :: a(n, n)
integer :: i, j
real(rk), parameter :: h = 0.01d0
a = 0.d0
! neglect the boundaries.
do i = 2, n-1
a(i, i-1) = (f(x(i), x(i-1)+h, x(i+1)) - f(x(i), x(i-1)-h, x(i+1)))/(2*h)
a(i, i) = (f(x(i)+h, x(i-1), x(i+1)) - f(x(i)-h, x(i-1), x(i+1)))/(2*h)
a(i, i+1) = (f(x(i), x(i-1), x(i+1)+h) - f(x(i), x(i-1), x(i+1)+h))/(2*h)
end do
end function jac
end module func
program jac_test
use fazang
use func
implicit none
integer, parameter :: n = 1000
integer i
real(rk) :: fx(n, n), x(n)
do i = 1, n
x(i) = 1.5d0 * i
end do
fx = jac(x, n)
end program jac_test
```

which takes

```
1/1 jacobian_fd OK 0.01s
```

Now brute force AD using the library I’m working on.

```
module func
use fazang
implicit none
contains
function f(x, n) result(fx)
integer, intent(in) :: n
type(var), intent(in) :: x(:)
type(var) :: fx(n)
integer :: i
fx(1) = x(1)*x(1) + x(2)*x(2)
fx(n) = x(n)*x(n) - x(n-1)*x(n-1)
do i = 2, n-1
fx(i) = x(i)*x(i) + x(i-1)*x(i-1) + x(i+1)*x(i+1)
end do
end function f
end module func
program jac_test
use fazang
use func
implicit none
integer, parameter :: n = 1000
integer i
real(rk) :: fx(n, n+1), x(n)
do i = 1, n
x(i) = 1.5d0 * i
end do
fx = jacobian(f, n, x)
end program jac_test
```

which takes

```
1/1 jacobian_ad OK 3.31s
```

The cause of performance gap is that in AD the jacobian is taken w.r.t. **every** x entry, without considering the sparsity.