JuliaGeo / Proj.jl

Julia wrapper for the PROJ cartographic projections library
MIT License
48 stars 9 forks source link

dynamic dispatch in Proj.proj_trans #104

Closed ctessum closed 2 months ago

ctessum commented 4 months ago

Hello,

I'm using Proj in some code where I'm trying to avoid allocations and dynamic dispatches for performance. To assist with this, I'm using the AllocCheck.jl packages, which finds allocations and dynamic dispatches in the code. So this example:

using Proj
using AllocCheck

const pj = Proj.proj_create_crs_to_crs(
    "EPSG:4326",  # source
    "+proj=utm +zone=32 +datum=WGS84",  # target, also EPSG:32632
)
const a = Proj.proj_coord(12, 55)

Proj.proj_trans(pj, Proj.PJ_FWD, a)

@check_allocs checkf(a) = Proj.proj_trans(pj, Proj.PJ_FWD, a)

try
    checkf(a)
catch err
    err.errors
end

gives the following error message:

Dynamic dispatch in ....julia/packages/Proj/YfK57/src/libproj.jl:494
  | @ccall libproj.proj_trans(P::Ptr{PJ}, direction::PJ_DIRECTION, coord::Coord)::Coord

Stacktrace:
 [1] proj_trans
   @ ~/.julia/packages/Proj/YfK57/src/libproj.jl:494 [inlined]
 [2] var"##checkf#260"(a::Coord)
 ....

I don't know enough about @ccall to be able to tell what the issue is, if there is one. Any suggestions would be appreciated!

rafaqz commented 4 months ago

Did you try looking at a profile? E.g. ProfileView.jl? It will show allocs and instability in yellow and red

ctessum commented 4 months ago

I tried looking at at with JET, but nothing shows up there. It seems like it's something specific to that @ccall line, and my guess is that maybe JET doesn't analyze @ccall but maybe AllocCheck.jl does, but I really don't know.

rafaqz commented 4 months ago

I mean a sampling profiler like ProfileView.jl. Thats the best tool for this job because it will instantly show where the allocations are coming from. Just run that code a lot of times inside a function with a loop in it, and put @profview in front of the function call.

ctessum commented 4 months ago

I mean, AllocCheck is pretty clear about where it's happening, line 494 of Proj.jl. I just don't know why it's a dynamic dispatch, or how it would be fixed.

rafaqz commented 4 months ago

But... the profile will show if its type instability (boxing) causing allocation, or just regular allocation, red or yellow in the profile. (Likely red given dynamic dispatch warning). It also gives context you need to understand type instability/dynamic dispatch.

They are totally unrelated reasons for allocation and totally different fixes.

If its red, use Cthulu.jl @descend to look around at types where you see problems in the profile. descend_clicked() after clicking the profile can help get there quicker.

If yellow, investigate possible allocations like Ref in code.

Compared to the ProfileView/Cthulhu combo AllocCheck is a blunt instrument. You need to see the context.

asinghvi17 commented 2 months ago

I can't see this allocation or any instability in ProfileView from my end. I do see the same error from AllocCheck, though.

Perhaps the "dynamic dispatch" here is really triggering on the @ccall? A BenchmarkTools benchmarking of the code in that function shows no allocations, so this might be a false alarm.


julia> @benchmark Proj.proj_trans($pj, $(Proj.PJ_FWD), $a)
BenchmarkTools.Trial: 10000 samples with 911 evaluations.
 Range (min … max):  114.572 ns …  2.863 μs  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     120.518 ns              ┊ GC (median):    0.00%
 Time  (mean ± σ):   123.166 ns ± 33.619 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  ▃  ▂▃▅█▄▄▄▆▆▅▄▂▂▁▁▁▁▁                                        ▂
  █▆▄█████████████████████▇▇▇▇▇▇▇▆▅▆▆▆▆▆▅▆▅▅▆▆▆▅▅▄▅▄▅▄▄▄▃▄▄▄▄▄ █
  115 ns        Histogram: log(frequency) by time       158 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.
rafaqz commented 2 months ago

I can't see this allocation or any instability in ProfileView from my end

If there is nothing in the profile that suggests there isn't actually a problem. Time of 100ns and no allocs in @btime also suggests that.