Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "TimerOutputComparisons"
uuid = "59c811e7-adb4-4b01-a05e-50a2dd0b99a2"
version = "1.0.0"
version = "1.0.1"
authors = ["John Omotani <john.omotani@ukaea.uk> and contributors"]

[deps]
Expand Down
65 changes: 55 additions & 10 deletions src/TimerOutputComparisons.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ const possible_use_data = (:ncalls, :time, :allocs)

"""
compare_timers(timers::Union{AbstractString,Tuple{<:AbstractString,<:AbstractString},Tuple{TimerOutput,<:AbstractString}}...;
flatten::Bool=false, averages::Bool=false,
flatten::Bool=false, averages::Bool=false, exclusive::Bool=false,
save_as::Union{AbstractString,Nothing}=nothing,
use_data=possible_use_data, legend::Bool=true, root=nothing)
use_data=possible_use_data, legend::Bool=true, root=nothing,
datainspector_kwargs::Union{Dict,NamedTuple}=Dict())

Make a plot comparing `timers`. For `t` in `timers`:
* if `t` is an AbstractString, load a TimerOutput from the file named `t` using
Expand All @@ -72,6 +73,9 @@ If `flatten=true`, the TimerOutput objects are flattened with `TimerOutputs.flat
If `averages=true`, then the time per call and allocs per call will be plotted instead of
the total time and total allocs for each timer.

If `exclusive=true`, then subtract the times from any nested timers from each timer. This
should remove any double-counting of times.

If a file-name is passed to `save_as` the plots are saved instead of being displayed
interactively. For example, `save_as="foo.png"` would result in the plots being saved as
"foo_ncalls.png", "foo_time.png", and "foo_allocs.png".
Expand All @@ -84,6 +88,9 @@ To remove the legend, pass `legend=false`.
If you only want to use_data some (possibly nested) sub-timer, pass its name as a String,
Tuple or Vector to `root`. For example if the top-level TimerOutput is `to`, passing
`root=("foo", "bar")` will use_data only the timers in `to["foo"]["bar"]`.

`datainspector_kwargs` are passed as the keyword arguments to the `DataInspector()` call
that creates the tooltips.
"""
compare_timers

Expand All @@ -102,9 +109,14 @@ function compare_timers(timers::Union{AbstractString,<:Tuple{AbstractString,Abst
return compare_timers(Tuple(get_timer(t) for t ∈ timers)...; kwargs...)
end
function compare_timers(timers::Tuple{TimerOutput,String}...;
flatten::Bool=false, averages::Bool=false,
flatten::Bool=false, averages::Bool=false, exclusive::Bool=false,
save_as::Union{AbstractString,Nothing}=nothing,
use_data=possible_use_data, legend::Bool=true, root=nothing)
use_data=possible_use_data, legend::Bool=true, root=nothing,
datainspector_kwargs::Union{Dict,NamedTuple}=Dict())

if flatten && exclusive
error("Cannot use `flatten=true` and `exclusive=true` at the same time.")
end

if isa(use_data, Symbol)
use_data = (use_data,)
Expand Down Expand Up @@ -176,10 +188,11 @@ function compare_timers(timers::Tuple{TimerOutput,String}...;

if root !== nothing
plot_single_timer!(ax_ncalls, ax_time, ax_allocs, to_list, root[end:end], xticks,
averages, true)
averages, exclusive, true)
end
for name ∈ timer_names
plot_single_timer!(ax_ncalls, ax_time, ax_allocs, to_list, name, xticks, averages)
plot_single_timer!(ax_ncalls, ax_time, ax_allocs, to_list, name, xticks, averages,
exclusive)
end

if legend
Expand All @@ -200,7 +213,7 @@ function compare_timers(timers::Tuple{TimerOutput,String}...;
backend = Makie.current_backend()
for fig in (fig_ncalls, fig_time, fig_allocs)
if fig !== nothing
DataInspector(fig)
DataInspector(fig; datainspector_kwargs...)
display(backend.Screen(), fig)
end
end
Expand Down Expand Up @@ -232,7 +245,7 @@ function get_single_timer(to::TimerOutput, name::Vector{String})
end

function plot_single_timer!(ax_ncalls, ax_time, ax_allocs, to_list, name::Vector{String},
xticks, averages, is_root::Bool=false)
xticks, averages, exclusive, is_root::Bool=false)
if is_root
this_timer_list = to_list
else
Expand All @@ -254,9 +267,41 @@ function plot_single_timer!(ax_ncalls, ax_time, ax_allocs, to_list, name::Vector
inspector_label=(self,i,p) -> "$(self.label[])\n$(xtick_values[round(Int64, p[1])]): ncalls=$(ncalls_values[round(Int64, p[1])])")
end

function get_time(t)
if t === nothing
return NaN
end

tval = TimerOutputs.time(t)

if exclusive
for subtimer ∈ values(t.inner_timers)
tval -= TimerOutputs.time(subtimer)
end
end

return tval * 1.0e-6
end

function get_alloc(t)
if t === nothing
return NaN
end

aval = TimerOutputs.allocated(t)

if exclusive
for subtimer ∈ values(t.inner_timers)
aval -= TimerOutputs.allocated(subtimer)
end
end

return aval / 1024
end

# Convert times from ns to ms.
if ax_time !== nothing
time_values = [t === nothing ? NaN : TimerOutputs.time(t) * 1.0e-6 for t ∈ this_timer_list]
time_values = [get_time(t) for t ∈ this_timer_list]
if averages
time_values ./= ncalls_values
end
Expand All @@ -266,7 +311,7 @@ function plot_single_timer!(ax_ncalls, ax_time, ax_allocs, to_list, name::Vector
end

if ax_allocs !== nothing
allocs_values = [t === nothing ? NaN : TimerOutputs.allocated(t) / 1024 for t ∈ this_timer_list]
allocs_values = [get_alloc(t) for t ∈ this_timer_list]
if averages
allocs_values ./= ncalls_values
end
Expand Down