包装 TimerOutputs 宏

Wrapping TimerOutputs macros

我有一种情况,如果有一个变量 to 会很方便,它可以是 TimerOutputnothing。我有兴趣提供一个宏,它采用与来自 TimerOutputs 的 @timeit 相同的参数(例如 @timeit to "time spent" s = foo())。因为 to 可能设置为 nothing,我不能简单地禁用 TimerObject。

如果设置了 to,我的偏好是将参数传递给 @timeit,但可以接受调用 timer_expr(__module__, false, args...).

如果未设置 to,我只想 return 剩余的参数(可能类似于 args[3:end])作为表达式。

我已经纠结了一天左右,可以单独处理每个案例。对于我不涉及 TimerOutput 的情况,这似乎有效:

macro no_timer(args...)
    args[3:end][1]
end

对于我 使用 TimerOutput 的情况,我可以这样做:

macro with_timer(args...)
    timer_expr(__module__, false, args...)
end

不足为奇,因为这正是 @timeit 所做的。

我还没有想出如何在一个宏中处理这两种情况。我通过将所有内容包装在三元运算符中获得了最接近的结果 - 即 return :(isnothing($(args[1])) ? <expression stuff> : <TimerOutput stuff>),但是存在某种程度的抽象不匹配我还没有解开。

附录:我得出的结论是我的原始框架是一个“X-Y”问题。我实际上并不需要一个新的宏来解决我的问题 - 因此我接受了我所做的答案。也就是说,令我震惊的是,所提供的两个答案都远离定义宏。

您已经在 TimerOuputs 中拥有此功能。 只需使用 disable_timer!enable_timer! 方法。

julia> const to = TimerOutput();

julia> disable_timer!(to);

julia> @timeit to "sleep" sleep(0.02)

julia> to
 ────────────────────────────────────────────────────────────────────
                            Time                    Allocations
                   ───────────────────────   ────────────────────────
 Tot / % measured:      23.6s /   0.0%            464KiB /   0.0%

 Section   ncalls     time    %tot     avg     alloc    %tot      avg
 ────────────────────────────────────────────────────────────────────
 ────────────────────────────────────────────────────────────────────

julia> enable_timer!(to);


julia> @timeit to "sleep" sleep(0.02)

julia> to
 ────────────────────────────────────────────────────────────────────
                            Time                    Allocations
                   ───────────────────────   ────────────────────────
 Tot / % measured:      37.3s /   0.1%            777KiB /   0.0%

 Section   ncalls     time    %tot     avg     alloc    %tot      avg
 ────────────────────────────────────────────────────────────────────
 sleep          1   22.6ms  100.0%  22.6ms      320B  100.0%     320B
 ────────────────────────────────────────────────────────────────────

您可以定义自己更具体的宏调用的 timer_expr 函数版本,其中返回的表达式包含对 to 参数是否为 nothing 的检查,然后执行合适的东西。


import TimerOutputs: timer_expr

function timer_expr(m::Module, is_debug::Bool, to::Symbol, label::String, ex::Expr)
  unescaped(ex) = ex.head == :escape ? ex.args[1] : ex

  # this is from the original timer_expr functions, 
  #  to be used when `to` isn't nothing
  timer_ex = TimerOutputs.is_func_def(ex) ? 
                    unescaped(TimerOutputs.timer_expr_func(m, is_debug, to, ex, label)) : 
                    TimerOutputs._timer_expr(m, is_debug, to, label, ex)
  
  cond_ex = esc(:(if isnothing($to)
      $ex
    else
      placeholder  # dummy symbol, to be replaced
    end))

  # args[3] = "else" section, 
  #   the placeholder is args[2] within that
  unescaped(cond_ex).args[3].args[2] = timer_ex

  cond_ex
end