Note that you can also use the “use functions as global variables” trick.
mode() = :debug
function myfunc()
if mode() == :debug
# do this only in debug mode
println("debugging...")
end
println("regular computation")
end
This gives
julia> myfunc()
debugging...
regular computation
julia> @code_typed myfunc()
CodeInfo(
1 ─ goto #3 if not true
2 ─ invoke Main.println("debugging..."::String)::Any
3 ┄ %3 = invoke Main.println("regular computation"::String)::Nothing
└── return %3
) => Nothing
Redefining, say, mode() = :live
will automatically trigger recompilation of myfunc()
and, via constant propagation, eliminate the debug branch entirely:
julia> mode() = :live
mode (generic function with 1 method)
julia> myfunc()
regular computation
julia> @code_typed myfunc()
CodeInfo(
1 ─ nothing::Nothing
│ %2 = invoke Main.println("regular computation"::String)::Nothing
└── return %2
) => Nothing
One advantage of this approach compared to attempting to achieve the same with global variables is that you can’t assign variables in other modules but you can redefine functions in other modules. See GitHub - JuliaPerf/STREAMBenchmark.jl: A version of the STREAM benchmark which measures the sustainable memory bandwidth., where I tell the user to redefine STREAMBenchmark.avxt() = true
to make my package STREAMBenchmark use LoopVectorization for multithreading, as an example.