Hi everyone,
I am using TimerOutputs.jl
to time parts of my code with the @timeit
macro. The typical syntax looks like this:
using TimerOutputs
timer = TimerOutput()
@timeit timer "some_identifier" begin
# some code...
end
I would like to create a new macro called @my_timeit
that behaves as follows:
- Uses
@timeit
from TimerOutputs.jl
when timer
is a TimerOutput
instance.
- Executes the code block directly (without timing) if
timer == nothing
.
My questions are:
- Is it possible to define such a macro in Julia?
- If it is, would calling
@my_timeit nothing "some_identifier" begin ... end
add any overhead to the execution of somecode...
?
Thanks in advance for any guidance or suggestions!
There was a proposal for adding something like this to TimerOutputs.jl a few years ago, but it was never merged. But defining a separate macro as you propose may also work, I haven’t tried that.
If I need to time a function I can simply use a wrapper like this:
import TimerOutputs
function wrap_with_timer(f::Function, name::String, timer::Union{TimerOutput,Nothing})
if isnothing(timer)
return f
else
f_(args...; kwargs...) = @timeit timer name f(args...; kwargs...)
return f_
end
end
My problem is now just with blocks of codes (which I don’t want to convert to a function)
This seems to work:
macro my_timeit(timer, label, expr)
quote
if $(esc(timer)) === nothing
$(esc(expr))
else
@timeit $(esc(timer)) $(esc(label)) $(esc(expr))
end
end
end
The branch seems to be optimised away by the compiler, so using timer = nothing
is as fast as simply running the expression without @timeit
.
Thanks, it works perfectly! Any way to extend it to function definitions without my wrapper (the wrapper above creates problem with @my_timeit
)?
Not sure if I understood correctly, but the following variant allows annotating functions (there might be better ways of doing this):
macro my_timeit(timer, f)
f.head === :function || error("expected a function")
call = f.args[1]
@assert call.head === :call # e.g. :(f(x))
label = string(call.args[1]) # e.g. "f"
expr = f.args[2] # actual content of the function
@assert expr.head === :block
# Replace function content
f.args[2] = quote
if $timer === nothing
$expr
else
@timeit $timer $label $expr
end
end
f
end
This can be used as:
@my_timeit timer function f(x, y, ..., timer)
# stuff...
end