I guess even more improvements might come once that PR gets cleared and merged (thank you for that!), but here are the corrected benchmarks:
julia> @btime method1($spl, extrema(t)...)
5.940 ms (60 allocations: 9.16 MiB)
70.44577023545651
julia> @btime method2($spl, extrema(t)...)
1.424 ms (51046 allocations: 1.51 MiB)
70.44577026541424
julia> @btime method2a($spl)
134.311 μs (4983 allocations: 151.69 KiB)
70.44577024751408
julia> @btime method3($spl, $(gauss(15))...)
83.455 μs (3354 allocations: 101.50 KiB)
70.44577024750694
julia> @btime method4($spl, order=4, rtol=1e-5)
69.240 μs (2467 allocations: 75.44 KiB)
70.44577023146906
These have been compared so the relative accuracy is ~1e-10 (and I tried to fix method4
following your discussion, hope I didn’t mess it up):
function method1(spl, t1, t2)
tl = LinRange(t1, t2, 100_000)
xy = spl(tl)
Δxy = xy[:,2:end] - xy[:,1:end-1]
sum(norm.(eachcol(Δxy)))
end
function method2(spl, t1, t2)
res, _ = quadgk(t -> norm(derivative(spl, t)), t1, t2, rtol = 1e-8)
res
end
function method2a(spl)
knots = Dierckx.get_knots(spl)
s = 0.0
for (k1, k2) in zip(knots[1:end-1], knots[2:end])
res, _ = quadgk(t -> norm(derivative(spl, t)), k1, k2)
s += res
end
return s
end
function method3(spl, x_gauss, w_gauss)
knots = Dierckx.get_knots(spl)
integral = sum(eachindex(knots)[2:end]) do i
t1, t2 = knots[i-1], knots[i]
scale = (t2 - t1) / 2
sum(zip(x_gauss, w_gauss)) do ((xg, wg))
t = (xg + 1) * scale + t1 # map from (-1,1) to (t1,t2)
norm(derivative(spl, t)) * wg
end * scale
end
end
function method4(spl; kws...)
knots = Dierckx.get_knots(spl)
s, _ = quadgk(t -> norm(derivative(spl, t)), knots; kws...)
return s
end
row = df[5, :]
spl = row.spl
t = row.t
s, _ = quadgk(t -> norm(derivative(spl, t)), extrema(t)..., rtol = 1e-9)
rel(x) = (x - s)/s
rel(method1(spl, extrema(t)...))
rel(method2(spl, extrema(t)...))
rel(method2a(spl))
rel(method3(spl, gauss(15)...))
rel(method4(spl, order=4, rtol=1e-5))