Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,10 @@ By default, debug timings are disabled, and this conditional should be optimized
If a user calls `TimerOutputs.enable_debug_timings(<module>)`, the `<module>.timeit_debug_enabled()` method will be redefined, causing all dependent methods to be recompiled within that module.
This may take a while, and hence is intended only for debugging usage, however all calls to `@timeit_debug` (within that Module) will thereafter be enabled.

As an alternative to `@timeit_debug`, this package also provides a `NullTimer` type that allows disabling all timings with zero overhead.
This type represents a dummy timer which acts as a drop-in replacement for regular `TimerOutput`s.
From a user's perspective, it suffices to replace `to = TimerOutput()` by `to = NullTimer()` at the beginning of their code to fully disable timers.

## Author

Kristoffer Carlsson - @KristofferC
Expand Down
30 changes: 30 additions & 0 deletions src/NullTimer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
struct NullTimer <: AbstractTimerOutput
name::String
NullTimer(label::String = "root") = new(label)
end

isdummy(::NullTimer) = true

Base.copy(to::NullTimer) = NullTimer(to.name)

# Add definitions for exported methods
reset_timer!(to::NullTimer) = to
timeit(f::Function, ::NullTimer, ::String) = f()
enable_timer!(::NullTimer) = true
disable_timer!(::NullTimer) = false
flatten(to::NullTimer) = to

ncalls(::NullTimer) = 0
time(::NullTimer) = 0
allocated(::NullTimer) = 0
tottime(::NullTimer) = 0
totallocated(::NullTimer) = 0

complement!(to::NullTimer) = to

Base.haskey(::NullTimer, ::String) = false
Base.getindex(::NullTimer, key::String) = throw(KeyError(key))

function Base.show(io::IO, to::NullTimer)
print(io, "Timer \"", to.name, "\" is a dummy timer")
end
40 changes: 26 additions & 14 deletions src/TimerOutput.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ end
###############
# TimerOutput #
###############
mutable struct TimerOutput

abstract type AbstractTimerOutput end

isdummy(::AbstractTimerOutput) = false

mutable struct TimerOutput <: AbstractTimerOutput
start_data::TimeData
accumulated_data::TimeData
inner_timers::Dict{String,TimerOutput}
Expand Down Expand Up @@ -201,20 +206,24 @@ function _timer_expr(m::Module, is_debug::Bool, to::Union{Symbol, Expr, TimerOut
@gensym local_to enabled accumulated_data b₀ t₀ val
timeit_block = quote
$local_to = $to
$enabled = $local_to.enabled
if $enabled
$accumulated_data = $(push!)($local_to, $label)
if Main.hasfield(typeof($local_to), :enabled) === false
$val = $ex
else
$enabled = $local_to.enabled
if $enabled
$accumulated_data = $(push!)($local_to, $label)
end
$b₀ = $(gc_bytes)()
$t₀ = $(time_ns)()
$(Expr(:tryfinally,
:($val = $ex),
quote
if $enabled
$(do_accumulate!)($accumulated_data, $t₀, $b₀)
$(pop!)($local_to)
end
end))
end
$b₀ = $(gc_bytes)()
$t₀ = $(time_ns)()
$(Expr(:tryfinally,
:($val = $ex),
quote
if $enabled
$(do_accumulate!)($accumulated_data, $t₀, $b₀)
$(pop!)($local_to)
end
end))
$val
end

Expand Down Expand Up @@ -357,6 +366,9 @@ notimeit_expr(ex::Expr) = notimeit_expr(:($(TimerOutputs.DEFAULT_TIMER)), ex)
function notimeit_expr(to, ex::Expr)
return quote
local to = $(esc(to))
if isdummy(to)
return $(esc(ex))
end
local enabled = to.enabled
$(disable_timer!)(to)
local val
Expand Down
2 changes: 2 additions & 0 deletions src/TimerOutputs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ using ExprTools
import Base: show, time_ns
export TimerOutput, @timeit, @timeit_debug, reset_timer!, print_timer, timeit,
enable_timer!, disable_timer!, @notimeit
export AbtractTimerOutput, NullTimer

# https://github.com/JuliaLang/julia/pull/33717
if VERSION < v"1.4.0-DEV.475"
Expand All @@ -21,6 +22,7 @@ using Printf


include("TimerOutput.jl")
include("NullTimer.jl")
include("show.jl")
include("utilities.jl")

Expand Down
6 changes: 3 additions & 3 deletions src/show.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
print_timer(; kwargs...) = print_timer(stdout; kwargs...)
print_timer(to::TimerOutput; kwargs...) = print_timer(stdout, to; kwargs...)
print_timer(to::AbstractTimerOutput; kwargs...) = print_timer(stdout, to; kwargs...)
print_timer(io::IO; kwargs...) = print_timer(io, DEFAULT_TIMER; kwargs...)
print_timer(io::IO, to::TimerOutput; kwargs...) = (show(io, to; kwargs...); println(io))
print_timer(io::IO, to::AbstractTimerOutput; kwargs...) = (show(io, to; kwargs...); println(io))

Base.show(to::TimerOutput; kwargs...) = show(stdout, to; kwargs...)
Base.show(to::AbstractTimerOutput; kwargs...) = show(stdout, to; kwargs...)
function Base.show(io::IO, to::TimerOutput; allocations::Bool = true, sortby::Symbol = :time, linechars::Symbol = :unicode, compact::Bool = false, title::String = "")
sortby in (:time, :ncalls, :allocations, :name) || throw(ArgumentError("sortby should be :time, :allocations, :ncalls or :name, got $sortby"))
linechars in (:unicode, :ascii) || throw(ArgumentError("linechars should be :unicode or :ascii, got $linechars"))
Expand Down
41 changes: 41 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -511,3 +511,44 @@ TimerOutputs.enable_debug_timings(@__MODULE__)
@test ncalls(subto["baz"]) == 2
end
end

@testset "dummy timers" begin
to = TimerOutput()
nt = NullTimer()

@test repr(nt) == "Timer \"root\" is a dummy timer"

let io = IOBuffer()
print_timer(io, nt)
@test String(take!(io)) == "Timer \"root\" is a dummy timer\n"
end

@test copy(nt) == nt
@test reset_timer!(nt) === nt
@test enable_timer!(nt) == true
@test disable_timer!(nt) == false
@test flatten(nt) === nt

foo(x) = x + x
@timeit tt foo(x, tt) = foo(x)
@test foo(4) == foo(4, to)
@test foo(4) == foo(4, nt)

@test @notimeit(nt, foo(3)) == foo(3)

@timeit to "sin" sin(3)
@timeit nt "sin" sin(3)

@test haskey(to, "sin")
@test !haskey(nt, "sin")

@test to["sin"] isa TimerOutput
@test_throws KeyError("sin") nt["sin"]

@test ncalls(nt) == 0
@test TimerOutputs.time(nt) == 0
@test TimerOutputs.allocated(nt) == 0
@test TimerOutputs.tottime(nt) == 0
@test TimerOutputs.totallocated(nt) == 0
@test TimerOutputs.complement!(nt) === nt
end