On upgrading to Julia from 1.8.5. to 1.9.0 I noticed code in one of my packages …slow down significantly. (I've already highlighted a type inference issue that I found. This is an entirely separate issue.) After upgrading, the following code suddenly causes a lot of memory allocations. This is the simplest code which replicates the problem, but most minor changes will suddenly cause all the allocations to disappear:
```julia
using StaticArrays, ForwardDiff, BenchmarkTools, JET
function rodrigues(x::T, y::T, z::T) where T
if x == 0 && y == 0 && z == 0
# Short cut for derivatives at identity
return SMatrix{3, 3, T, 9}(T(1), z, -y, -z, T(1), x, y, -x, T(1))
end
theta2 = x * x + y * y + z * z
cosf = T(0.5)
sinc = T(1)
if theta2 > T(2.23e-16)
theta = sqrt(theta2)
sinc, cosf = sincos(theta)
cosf -= 1
sinc /= theta
cosf /= -theta2
end
a = x * y * cosf
b = sinc * z
c = x * z * cosf
d = sinc * y
e = y * z * cosf
f = sinc * x
return SMatrix{3, 3, T, 9}((x * x - theta2) * cosf + 1, a + b, c - d,
a - b, (y * y - theta2) * cosf + 1, e + f,
c + d, e - f, (z * z - theta2) * cosf + 1)
end
struct BarrelDistortion{T}
k1::T
k2::T
end
update(var::BarrelDistortion, updatevec, start=1) = BarrelDistortion(var.k1 + updatevec[start], var.k2 + updatevec[start+1])
function ideal2distorted(lens::BarrelDistortion, x)
z = x' * x
z = z * (lens.k1 + z * lens.k2) + 1
return x * z
end
struct SimpleCamera{T}
f::T
end
SimpleCamera(v::T) where T = SimpleCamera{T}(v::T)
update(var::SimpleCamera, updatevec, start=1) = SimpleCamera(max(var.f, floatmin(var.f)) * exp(updatevec[start]))
struct Point3D{T}
v::SVector{3, T}
end
Point3D(x, y, z) = Point3D(SVector{3}(x, y, z))
Point3D() = Point3D(SVector{3}(0., 0., 0.))
update(var::Point3D, updatevec, start=1) = Point3D(var.v + updatevec[StaticArrays.SUnitRange(0, 2) .+ start])
project(x::Point3D{T}) where T = SVector{2, T}(x.v[1], x.v[2]) ./ x.v[3]
struct Rotation{T}
m::SMatrix{3, 3, T, 9}
end
Rotation(x, y, z) = Rotation(rodrigues(x, y, z))
update(var::Rotation, updatevec, start=1) = transform(Rotation(updatevec[start], updatevec[start+1], updatevec[start+2]), var)
transform(rota::Rotation, rotb::Rotation) = Rotation(rota.m * rotb.m)
struct EffPose3D{T}
rot::Rotation{T}
camcenter::Point3D{T}
end
EffPose3D(rx, ry, rz, cx, cy, cz) = EffPose3D(Rotation(rx, ry, rz), Point3D(cx, cy, cz))
update(var::EffPose3D, updatevec, start=1) = EffPose3D(update(var.rot, updatevec, start), update(var.camcenter, updatevec, start+3))
transform(pose::EffPose3D, point::Point3D) = Point3D(pose.rot.m * (point.v - pose.camcenter.v))
# Description of BAL image, and function to transform a landmark from world coordinates to pixel coordinates
struct Image{T}
pose::EffPose3D{T}
sensor::SimpleCamera{T}
lens::BarrelDistortion{T}
end
update(var::Image, updatevec, start=1) = Image(update(var.pose, updatevec, start), update(var.sensor, updatevec, start+6), update(var.lens, updatevec, start+7))
function Image(rx::T, ry::T, rz::T, tx::T, ty::T, tz::T, f::T, k1::T, k2::T) where T
R = Rotation(rx, ry, rz)
return Image{T}(EffPose3D(R, Point3D(R.m' * -SVector(tx, ty, tz))), SimpleCamera(f), BarrelDistortion(k1, k2))
end
transform(im::Image, X::Point3D) = ideal2distorted(im.lens, -project(Point3D(im.pose.rot.m * (X.v - im.pose.camcenter.v))))
computeresjac(vars...) = ForwardDiff.jacobian(z -> transform(update(vars[1], z, 1), update(vars[2], z, 10)), zeros(SVector{12, Float64}))
function mytest()
im = Image(1., 1., 1., 1., 1., 1., 1., 1., 1.)
lm = Point3D(0., 0., 0.)
@btime computeresjac($im, $lm)
show(JET.@report_opt computeresjac(im, lm))
end
mytest()
```