Optional debugging and logging
For debugging purposes, it may sometimes be beneficial to include logging statements in a function, for example
@check_allocs function myfun(verbose::Bool)
a = 0.0
for i = 1:3
a = a + i
verbose && @info "a = $a"
end
end
Here, the printing of some relevant information is only performed if verbose = true
. While the printing is optional, and not performed if verbose = false
, check_allocs
operates on types rather than values, i.e., check_allocs
only knows that the argument is of type Bool
, not that it may have the value false
:
julia> myfun(false)
ERROR: @check_allocs function encountered 97 errors (89 allocations / 8 dynamic dispatches).
Indeed, this function was determined to potentially allocate memory.
To allow such optional features while still being able to prove that a function does not allocate if the allocating features are turned off, we may lift the value true
into the type domain, we do this by means of the Val
type:
function typed_myfun(::Val{verbose}) where verbose
a = 0.0
for i = 1:3
a = a + i
verbose && @info "a = $a"
end
end
length(check_allocs(typed_myfun, (Val{false},)))
0
The compiler, and thus also AllocCheck, now knows that the value of verbose
is false
, since this is encoded in the type Val{false}
. The compiler can use this knowledge to figure out that the @info
statement won't be executed, and thus prove that the function will not allocate memory.
The user may still use this function with the debug print enabled by calling it like
typed_myfun(Val{true}())
[ Info: a = 1.0
[ Info: a = 3.0
[ Info: a = 6.0
Advanced: Constant propagation
Sometimes, code written without this trick will still work just fine with AllocCheck.
That's because in some limited scenarios, the compiler is able to use constant propagation to determine what path through a program will be taken based on the value of constants.
We demonstrate this effect below, where the value verbose = false
is hard-coded into the function:
@check_allocs function constant_myfun()
verbose = false
a = 0.0
for i = 1:3
a = a + i
verbose && @info "a = $a"
end
return a
end
constant_myfun()
6.0
When looking at constant_myfun
, the compiler knows that verbose = false
since this constant is hard coded into the program. Sometimes, the compiler can even propagate constant values all the way into called functions.
This is useful, but it's not guaranteed to happen in general. The Val{T}
trick described here ensures that the variable is propagated as a constant everywhere it is required.