Strange allocation in closure

Hi,

I observe an allocation involving a closure that I do not understand.
Consider the following minimal working example:

julia> function outer(a)
           inner(b, c) = b, c
           a, inner
       end
outer (generic function with 1 method)

julia> a, inner = outer(5)
(5, inner)

julia> @allocations outer(5)
0

julia> inner(5, 8)
(5, 8)

julia> @allocations inner(5, 8)
1

julia> @allocated inner(5, 8)
32

Note that inner() doesn’t really close over anything. Output of @code_warntype of both, outer(5) and inner(5, 8) looks fine.
Is this a false positive of the @allocated macro?

versioninfo
julia> versioninfo()
Julia Version 1.9.3
Commit bed2cd540a1 (2023-08-24 14:43 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 × 11th Gen Intel(R) Core(TM) i5-1145G7 @ 2.60GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-14.0.6 (ORCJIT, tigerlake)
  Threads: 1 on 8 virtual cores

When you do this step, you bound inner to a untyped global variable. That’s the source of the allocation.

Here’s an example where it’s bound to a local variable and you can see there’s no allocation:

julia> function outer(a)
           inner(b, c) = b, c
           a, inner
       end;

julia> let (a, inner) = outer(5)
           @allocated inner(5, 8)
       end
0
5 Likes

Seems like a false-positive (but I don’t understand how these come about).
The generated assembly is identical for

julia> f(a,b) = return a,b
julia> @code_native inner(5,8)
julia> @code_native f(5,8) # identical output to above

and the latter shows no allocations.

Edit: @Mason above explained where the allocation comes from.

1 Like

This makes it very explicit:

julia> function outer(a)
           inner(b, c) = b, c
           a, inner
       end;

julia> a, unstable_inner = outer(5)
(5, var"#inner#1"())

julia> const inner = unstable_inner
inner (generic function with 1 method)

julia> @allocated unstable_inner(5,8)
27728

julia> @allocated inner(5,8)
0
2 Likes