# Need help understanding allocations while calling external C library (Proj)

**URL:** <https://discourse.julialang.org/t/need-help-understanding-allocations-while-calling-external-c-library-proj/101133>\
**Category:** Performance\
**Tags:** memory-allocation, allocations\
**Created:** [July 3, 2023, 3:25pm UTC](https://discourse.julialang.org/t/need-help-understanding-allocations-while-calling-external-c-library-proj/101133 "2023-07-03T15:25:28Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![disberd](https://avatars.discourse-cdn.com/v4/letter/d/8edcca/32.png) [@disberd](https://discourse.julialang.org/u/disberd)\
**Post date:** [July 3, 2023, 3:25pm UTC](https://discourse.julialang.org/t/need-help-understanding-allocations-while-calling-external-c-library-proj/101133/1 "2023-07-03T15:25:28Z")

</div>

Hello everyone,

I have been scratching my head around allocations I have in a package I am developing that I can’t seem to eliminate.

Moreover it seems that the allocation results I get from `@allocations` and `@btime` from BenchmarkTools differ.

I have been trying to narrow down some MWE as much as possible to still see unexplicaple (for me) allocations and I came up with a quite reduced code example that you can see below:

```julia
using BenchmarkTools
using Proj

latlon1 = (10.0, 100.0,)
latlon2 = (15.0, 140.0,)
dist, azi1, azi2 = (Ref(0.0) for _ in 1:3) |> collect

g = Ref{Proj.geod_geodesic}()
Proj.geod_init(g, 6371e3, 0.0)

# Version with output pointers as kwargs
geod_inverse_kwargs(g, lat1, lon1, lat2, lon2; dist, azi1, azi2) = Proj.geod_inverse(g, lat1, lon1, lat2, lon2, dist, azi1, azi2)

# Call first time to compile
Proj.geod_inverse(g, latlon1[1], latlon1[2], latlon2[1], latlon2[2], dist, azi1, azi2)
Proj.geod_inverse(g, latlon1..., latlon2..., dist, azi1, azi2)
geod_inverse_kwargs(g, latlon1[1], latlon1[2], latlon2[1], latlon2[2]; dist, azi1, azi2)
geod_inverse_kwargs(g, latlon1..., latlon2...; dist, azi1, azi2)

# Compute allocations on standard function, my obtained results as comment after each line
@allocations Proj.geod_inverse(g, latlon1[1], latlon1[2], latlon2[1], latlon2[2], dist, azi1, azi2)
# 4
@btime Proj.geod_inverse($g, $(latlon1[1]), $(latlon1[2]), $(latlon2[1]), $(latlon2[2]), $dist, $azi1, $azi2)
# 547.420 ns (0 allocations: 0 bytes)
@allocations Proj.geod_inverse(g, latlon1..., latlon2..., dist, azi1, azi2)
# 6
@btime Proj.geod_inverse($g, $(latlon1)..., $(latlon2)..., $dist, $azi1, $azi2)
# 550.930 ns (0 allocations: 0 bytes)
@allocations Proj.geod_inverse(g, 10.0, 100.0, 15.0, 140.0, dist, azi1, azi2)
# 0
@btime Proj.geod_inverse($g, 10.0, 100.0, 15.0, 140.0, $dist, $azi1, $azi2)
# 553.086 ns (0 allocations: 0 bytes)

# Compute allocations on kwarg function, my obtained results as comment after each line
@allocations geod_inverse_kwargs(g, latlon1[1], latlon1[2], latlon2[1], latlon2[2]; dist, azi1, azi2)
# 6
@btime geod_inverse_kwargs($g, $(latlon1[1]), $(latlon1[2]), $(latlon2[1]), $(latlon2[2]); dist=$dist, azi1=$azi1, azi2=$azi2)
# 548.511 ns (0 allocations: 0 bytes)
@allocations geod_inverse_kwargs(g, latlon1..., latlon2...; dist, azi1, azi2)
# 8
@btime geod_inverse_kwargs($g, $(latlon1)..., $(latlon2)...; dist=$dist, azi1=$azi1, azi2=$azi2)
# 551.813 ns (0 allocations: 0 bytes)
@allocations geod_inverse_kwargs(g, 10.0, 100.0, 15.0, 140.0; dist, azi1, azi2)
# 2
@btime geod_inverse_kwargs($g, 10.0, 100.0, 15.0, 140.0; dist=$dist, azi1=$azi1, azi2=$azi2)
# 553.021 ns (0 allocations: 0 bytes)

```

The code above just tries to compute the distance and bearing between two points on earth, with three slightly different ways of calling the internal function.

I also have a version where the pointers to the output are provided as kwargs as that was my original intended use, and I notice that in that case allocations go up by 2 in each call.

The `geod_inverse` function is quite simple as it seems to just forward the arguments to `@ccall`:

> <https://github.com/JuliaGeo/Proj.jl/blob/d88918ca72426245bcf52889854ad2aa08d1e7c8/src/libproj.jl#L2290-L2301>

Can someone help me understand why the allocations are present?

Edit: This is on julia 1.9, and I see this both on Linux and Windows.  
Edit2: Added signature with kwargs  
Edit3: Added results for `@btime`

---

<div class="post-metadata">

**Author:** ![disberd](https://avatars.discourse-cdn.com/v4/letter/d/8edcca/32.png) [@disberd](https://discourse.julialang.org/u/disberd)\
**Post date:** [July 4, 2023, 8:26am UTC](https://discourse.julialang.org/t/need-help-understanding-allocations-while-calling-external-c-library-proj/101133/2 "2023-07-04T08:26:16Z")

</div>

I have also tried using the VSCode allocations profiler and here are the results I get

The call with indexing the tuple seems to be creating allocations due to `getindex`

```julia
@profview_allocs for i in 1:10^5
    Proj.geod_inverse(g, latlon1[1], latlon1[2], latlon2[1], latlon2[2], dist, azi1, azi2) # This produces 4 allocations
end

```

gives:

 ![image](https://global.discourse-cdn.com/julialang/original/3X/0/d/0d10ac0a7bb829674e5cde2b07d7f7e15ef6afa1.png)

The call with splatting also adds allocations due to the GC of the 3 output `Ref` pluts the `Ref` containing `Proj.geod_geodesic`

```julia
@profview_allocs for i in 1:10^5
    Proj.geod_inverse(g, latlon1..., latlon2..., dist, azi1, azi2)
end

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/e/b/ebf3e16396be3a0c75c555583bbed3e432ec1780.png)

Finally, the call that does not allocated without kwargs, but does with kwargs seems to allocate due to GC of the output `Ref` and of an UnkownType

```julia
# Profile allocations on last signature with kwarg function
@profview_allocs for i in 1:10^5
    geod_inverse_kwargs(g, 10.0, 100.0, 15.0, 140.0; dist, azi1, azi2)
end

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/1/8/1835a79d1bbda62bd387c9c7299ef234e11373d0.png)

---

<div class="post-metadata">

**Author:** ![disberd](https://avatars.discourse-cdn.com/v4/letter/d/8edcca/32.png) [@disberd](https://discourse.julialang.org/u/disberd)\
**Post date:** [July 4, 2023, 4:23pm UTC](https://discourse.julialang.org/t/need-help-understanding-allocations-while-calling-external-c-library-proj/101133/3 "2023-07-04T16:23:18Z")

</div>

Strangely enough, I can’t seem to see allocations (or significant timing difference) with `@btime`.  
On the Linux server I run stuff into I get the following results (As comment after each line):

```julia
@allocations Proj.geod_inverse(g, latlon1[1], latlon1[2], latlon2[1], latlon2[2], dist, azi1, azi2)
# 4
@btime Proj.geod_inverse($g, $(latlon1[1]), $(latlon1[2]), $(latlon2[1]), $(latlon2[2]), $dist, $azi1, $azi2)
# 547.420 ns (0 allocations: 0 bytes)
@allocations Proj.geod_inverse(g, latlon1..., latlon2..., dist, azi1, azi2)
# 6
@btime Proj.geod_inverse($g, $(latlon1)..., $(latlon2)..., $dist, $azi1, $azi2)
# 550.930 ns (0 allocations: 0 bytes)
@allocations Proj.geod_inverse(g, 10.0, 100.0, 15.0, 140.0, dist, azi1, azi2)
# 0
@btime Proj.geod_inverse($g, 10.0, 100.0, 15.0, 140.0, $dist, $azi1, $azi2)
# 553.086 ns (0 allocations: 0 bytes)

# Compute allocations on kwarg function
@allocations geod_inverse_kwargs(g, latlon1[1], latlon1[2], latlon2[1], latlon2[2]; dist, azi1, azi2)
# 6
@btime geod_inverse_kwargs($g, $(latlon1[1]), $(latlon1[2]), $(latlon2[1]), $(latlon2[2]); dist=$dist, azi1=$azi1, azi2=$azi2)
# 548.511 ns (0 allocations: 0 bytes)
@allocations geod_inverse_kwargs(g, latlon1..., latlon2...; dist, azi1, azi2)
# 8
@btime geod_inverse_kwargs($g, $(latlon1)..., $(latlon2)...; dist=$dist, azi1=$azi1, azi2=$azi2)
# 551.813 ns (0 allocations: 0 bytes)
@allocations geod_inverse_kwargs(g, 10.0, 100.0, 15.0, 140.0; dist, azi1, azi2)
# 2
@btime geod_inverse_kwargs($g, 10.0, 100.0, 15.0, 140.0; dist=$dist, azi1=$azi1, azi2=$azi2)
# 553.021 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![disberd](https://avatars.discourse-cdn.com/v4/letter/d/8edcca/32.png) [@disberd](https://discourse.julialang.org/u/disberd)\
**Post date:** [July 4, 2023, 5:15pm UTC](https://discourse.julialang.org/t/need-help-understanding-allocations-while-calling-external-c-library-proj/101133/4 "2023-07-04T17:15:33Z")

</div>

As pointed out to me on [zulip](https://julialang.zulipchat.com/#narrow/stream/225542-helpdesk/topic/understanding.20unexpected.20allocations.20when.20using.20.40ccall/near/372368985), it seems the allocations of `@allocated` in this case are an artifact of executing this in global scope as a script.

I should have catched it but the weird results I was getting in actual more complex use case made me think that was not the issue here.

The lack of allocations with `@btime` should have made me realize that though.
