Using `@allocated` to track memory allocations


#1

I am having a hard time understanding allocation in some code I have. The minimal example is the code below:

function foo(c)
    v = c * 1
    v
end

c = 1.0

@show @allocated foo(c)
@show @allocated foo(c)
@show @allocated foo(c)

I would expect this code not to incur in any allocation. However, the result is

@allocated(foo(c)) = 16
@allocated(foo(c)) = 16
@allocated(foo(c)) = 16

Why is my understanding not correct?

Thanks!


#2

It’s just because you’re working in global scope. Try putting the call to @allocated inside a function to get a more representative value.


#3

What part is in global scope? does not foo(c) run entirely in its own scope?


#4

If you’re working at the REPL, then c itself is a global variable. It can’t have a concrete inferred type because you could reassign it to anything. But once you’re inside a function, even when called from the REPL, everything is fine. This is the same reason that we always tell people not to benchmark in global scope from there REPL.

Since manually wrapping things in a function can be annoying, I wrote a @wrappedalllcs macro that tries to do it for you. You might find this helpful: https://github.com/rdeits/TypedPolynomials.jl/blob/master/test/runtests.jl


#5

OK, thanks. I just fail to see why foo(c) would make reference to variables in the global scope.


#6

Because the function call accesses c – it’s an input after all. You can also declare that as const and get no allocations:

julia> const d = 1.0
1.0

julia> @show @allocated foo(d)
@allocated(foo(d)) = 0
0

#7

Thanks.

My (wrong) understanding was that foo(c) would be a self-contained piece of code that made no reference to the global variable, as if the current value of c had been read somewhere and given as an input for foo to be executed.


#8

Just to add a last comment to this discussion. Wrapping the call to the macro in a function yields no allocations, as expected.

julia> bar(c) = @allocated foo(c)
julia> @show bar(c)
bar(c) = 0
0