diff --git a/Project.toml b/Project.toml index f5c609f3c..fe2a3b9d1 100644 --- a/Project.toml +++ b/Project.toml @@ -54,6 +54,6 @@ RecipesBase = "1.1" TensorKit = "0.17.1" TensorKitManifolds = "0.7, 0.8" TensorOperations = "5.6.2" -TimerOutputs = "0.5.29" +TimerOutputs = "1" VectorInterface = "0.2, 0.3, 0.4, 0.5, 0.6" julia = "1.10" diff --git a/docs/src/changelog.md b/docs/src/changelog.md index be8bae968..c98934a5c 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -70,11 +70,15 @@ When releasing a new version, move the "Unreleased" changes to a new version sec - `correlator` now throws an `ArgumentError` when the sites are not ordered as `i < j`. Previously such a call only logged an `@error` and then continued into a contraction that is not the requested correlator. ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) +- TimerOutputs 1.x is now required. The timing tables printed at `verbosity > 3` use the new + layout (tree guides, heat bars) and additionally report per-section GC time. ### Deprecated ### Removed +- Support for TimerOutputs 0.5. + ### Fixed - `isfinite(::WindowMPOHamiltonian)` was undefined. ([#489](https://github.com/QuantumKitHub/MPSKit.jl/pull/489)) diff --git a/docs/src/lib/lib.md b/docs/src/lib/lib.md index 2d14635fe..6909d75ce 100644 --- a/docs/src/lib/lib.md +++ b/docs/src/lib/lib.md @@ -1,5 +1,9 @@ # Library documentation +```@meta +CurrentModule = MPSKit +``` + ```@autodocs Modules = [MPSKit, MPSKit.DynamicTols, MPSKit.Defaults] ``` diff --git a/src/MPSKit.jl b/src/MPSKit.jl index 29077215b..f900884ae 100644 --- a/src/MPSKit.jl +++ b/src/MPSKit.jl @@ -88,7 +88,7 @@ using Random using Base: @kwdef, @propagate_inbounds using LoggingExtras using OhMyThreads -using TimerOutputs: TimerOutput, @timeit, timeit, reset_timer!, disable_timer!, enable_timer! +using TimerOutputs: TimerOutput, NoTimerOutput, @timeit, timeit, print_timer # Includes # -------- diff --git a/src/algorithms/algorithm.jl b/src/algorithms/algorithm.jl index 8f7ba864b..6305ae323 100644 --- a/src/algorithms/algorithm.jl +++ b/src/algorithms/algorithm.jl @@ -23,10 +23,22 @@ end # TIMEROUTPUT utility # ------------------- -# Shared sentinel passed as the default `timeroutput` kwarg by functions that optionally accept a timer. -# `@timeit` is a no-op when the destination timer is disabled, so unrelated callers pay no instrumentation cost. -# The merge sites that mutate `timeroutput` must gate on `timeroutput.enabled` so they don't pollute this shared object. -const DISABLED_TIMER = let t = TimerOutput("DISABLED") - disable_timer!(t) - t + +timer_treepoint(::NoTimerOutput) = String[] +timer_treepoint(to::TimerOutput) = String[section.name for section in to.stack] + +subtimer(::NoTimerOutput) = NoTimerOutput() +subtimer(to::TimerOutput) = to.enabled ? TimerOutput() : NoTimerOutput() + +merge_subtimer!(::NoTimerOutput, ::NoTimerOutput; tree_point) = nothing +merge_subtimer!(::TimerOutput, ::NoTimerOutput; tree_point) = nothing +function merge_subtimer!(to::TimerOutput, sub::TimerOutput; tree_point) + to.enabled && merge!(to, sub; tree_point) + return nothing +end + +# `print_timer` is used over plain `show` to opt into the GC time column +struct TimerReport{T} + to::T end +Base.show(io::IO, r::TimerReport) = print_timer(io, r.to; gc = true) diff --git a/src/algorithms/grassmann.jl b/src/algorithms/grassmann.jl index 1f55dca38..7da1a0133 100644 --- a/src/algorithms/grassmann.jl +++ b/src/algorithms/grassmann.jl @@ -12,7 +12,7 @@ module GrassmannMPS using ..MPSKit using ..MPSKit: AbstractMPSEnvironments, InfiniteEnvironments, MultilineEnvironments, - AC_projection, recalculate!, TimerOutput, DISABLED_TIMER, @timeit, default_allocator + AC_projection, recalculate!, NoTimerOutput, @timeit, default_allocator using TensorOperations: AbstractBackend, DefaultBackend using TensorKit using OhMyThreads @@ -149,7 +149,7 @@ that the allocator it selects is inferable. function fg( state::FiniteMPS, operator::Union{O, LazySum{O}}, envs::AbstractMPSEnvironments = environments(state, operator, state); - timeroutput::TimerOutput = DISABLED_TIMER, + timeroutput = NoTimerOutput(), backend::AbstractBackend = DefaultBackend(), # accepted for interface uniformity, but unused: this gradient is serial regardless scheduler::Scheduler = SerialScheduler(), @@ -168,7 +168,7 @@ end function fg( state::InfiniteMPS, operator::Union{O, LazySum{O}}, envs::AbstractMPSEnvironments = environments(state, operator, state); - timeroutput::TimerOutput = DISABLED_TIMER, + timeroutput = NoTimerOutput(), backend::AbstractBackend = DefaultBackend(), scheduler::Scheduler = MPSKit.Defaults.scheduler[], ) where {O <: InfiniteMPOHamiltonian} @@ -190,7 +190,7 @@ end function fg( state::InfiniteMPS, operator::Union{O, LazySum{O}}, envs::AbstractMPSEnvironments = environments(state, operator, state); - timeroutput::TimerOutput = DISABLED_TIMER, + timeroutput = NoTimerOutput(), backend::AbstractBackend = DefaultBackend(), scheduler::Scheduler = MPSKit.Defaults.scheduler[], ) where {O <: InfiniteMPO} @@ -212,7 +212,7 @@ end function fg( state::MultilineMPS, operator::MultilineMPO, envs::MultilineEnvironments = environments(state, operator, state); - timeroutput::TimerOutput = DISABLED_TIMER, + timeroutput = NoTimerOutput(), backend::AbstractBackend = DefaultBackend(), scheduler::Scheduler = MPSKit.Defaults.scheduler[], ) diff --git a/src/algorithms/groundstate/dmrg.jl b/src/algorithms/groundstate/dmrg.jl index 9c996dbcb..b6aa8ef70 100644 --- a/src/algorithms/groundstate/dmrg.jl +++ b/src/algorithms/groundstate/dmrg.jl @@ -280,16 +280,18 @@ function find_groundstate!( ) # the sweep is serial, so a single allocator serves all local updates allocator = default_allocator(ψ, SerialScheduler()) - return _find_groundstate_sweep!(ψ, H, alg, envs, allocator) + + name = string(nameof(typeof(alg))) + timeroutput = alg.verbosity > 3 ? TimerOutput(name) : NoTimerOutput() + + return find_groundstate_sweep!(ψ, H, alg, envs, allocator, timeroutput) end -function _find_groundstate_sweep!( - ψ::AbstractFiniteMPS, H, alg::Union{DMRG, DMRG2}, envs, allocator +function find_groundstate_sweep!( + ψ::AbstractFiniteMPS, H, alg::Union{DMRG, DMRG2}, envs, allocator, timeroutput ) name = string(nameof(typeof(alg))) log = IterLog(name) - timeroutput = TimerOutput(name) - alg.verbosity > 3 || disable_timer!(timeroutput) Tr = real(scalartype(ψ)) n = _num_updates(alg, ψ) @@ -338,12 +340,12 @@ function _find_groundstate_sweep!( # truncation error rather than the (unreachable) bare `tol`. With no truncation # (`ϵ_truncs .= 0`, e.g. single-site/QR gauge) this reduces to the plain `ϵ_global ≤ tol`. if ϵ_global <= max(alg.tol, maximum(ϵ_truncs)) - @infov 4 timeroutput + @infov 4 TimerReport(timeroutput) @infov 2 logfinish!(log, iter, ϵ_global, expectation_value(ψ, H, envs)) break end if iter == alg.maxiter - @infov 4 timeroutput + @infov 4 TimerReport(timeroutput) @warnv 1 logcancel!(log, iter, ϵ_global, expectation_value(ψ, H, envs)) else @infov 3 logiter!(log, iter, ϵ_global, expectation_value(ψ, H, envs)) diff --git a/src/algorithms/groundstate/gradient_grassmann.jl b/src/algorithms/groundstate/gradient_grassmann.jl index 0159a0ef7..550edaac4 100644 --- a/src/algorithms/groundstate/gradient_grassmann.jl +++ b/src/algorithms/groundstate/gradient_grassmann.jl @@ -84,12 +84,17 @@ function find_groundstate( @warn "This is not fully supported - split the mps up in a sum of mps's and optimize separately" normalize!(ψ) - timeroutput = TimerOutput("GradientGrassmann") method_verbosity = hasproperty(alg.method, :verbosity) ? alg.method.verbosity : 0 - method_verbosity > 3 || disable_timer!(timeroutput) + # function barrier: the timer type must be concrete for the closures below to elide timing + timeroutput = method_verbosity > 3 ? TimerOutput("GradientGrassmann") : NoTimerOutput() # read the scheduler here rather than in `fg`, so that the allocator it selects is inferable scheduler = Defaults.scheduler[] + + return _find_groundstate(ψ, H, alg, envs, scheduler, timeroutput, method_verbosity) +end + +function _find_groundstate(ψ, H, alg::GradientGrassmann, envs, scheduler, timeroutput, method_verbosity) fg(x) = timeit( () -> GrassmannMPS.fg(x, H, envs; timeroutput, alg.backend, scheduler), timeroutput, "fg", @@ -117,7 +122,7 @@ function find_groundstate( ) LoggingExtras.withlevel(; verbosity = method_verbosity) do - @infov 4 timeroutput + @infov 4 TimerReport(timeroutput) end return x, envs, normgradhistory[end] diff --git a/src/algorithms/groundstate/idmrg.jl b/src/algorithms/groundstate/idmrg.jl index e57d11de2..1b9c3c494 100644 --- a/src/algorithms/groundstate/idmrg.jl +++ b/src/algorithms/groundstate/idmrg.jl @@ -71,21 +71,21 @@ Used as the `algorithm` argument of [`find_groundstate`](@ref), [`leading_bounda end # Internal state of the IDMRG algorithm -struct IDMRGState{S, O, E, T, A} +struct IDMRGState{S, O, E, T, TO, A} mps::S operator::O envs::E iter::Int ϵ::Float64 # TODO: Could be any <:Real energy::T - timeroutput::TimerOutput + timeroutput::TO allocator::A end function IDMRGState{T}( mps::S, operator::O, envs::E, iter::Int, ϵ::Float64, energy, - timeroutput::TimerOutput, allocator::A, - ) where {S, O, E, T, A} - return IDMRGState{S, O, E, T, A}( + timeroutput::TO, allocator::A, + ) where {S, O, E, T, TO, A} + return IDMRGState{S, O, E, T, TO, A}( mps, operator, envs, iter, ϵ, T(energy), timeroutput, allocator ) end @@ -98,8 +98,7 @@ function _find_groundstate_idmrg(mps, operator, alg::alg_type, envs) where {alg_ (length(mps) ≤ 1 && alg isa IDMRG2) && throw(ArgumentError("unit cell should be >= 2")) name = alg isa IDMRG ? "IDMRG" : "IDMRG2" log = IterLog(name) - timeroutput = TimerOutput(name) - alg.verbosity > 3 || disable_timer!(timeroutput) + timeroutput = alg.verbosity > 3 ? TimerOutput(name) : NoTimerOutput() mps = copy(mps) iter = 0 # the sweeps are serial, so one allocator serves the whole solve @@ -120,12 +119,12 @@ function _find_groundstate_idmrg(mps, operator, alg::alg_type, envs) where {alg_ return LoggingExtras.withlevel(; alg.verbosity) do for (mps, envs, ϵ, ΔE) in it if ϵ ≤ alg.tol - @infov 4 timeroutput + @infov 4 TimerReport(timeroutput) @infov 2 logfinish!(log, it.iter, ϵ, ΔE) break end if it.iter ≥ alg.maxiter - @infov 4 timeroutput + @infov 4 TimerReport(timeroutput) @warnv 1 logcancel!(log, it.iter, ϵ, ΔE) break end @@ -192,7 +191,7 @@ function localupdate_step!( end function _localupdate_sweep_idmrg!( - ψ, H, envs, alg_eigsolve, timeroutput::TimerOutput; + ψ, H, envs, alg_eigsolve, timeroutput; backend::AbstractBackend = DefaultBackend(), allocator = DefaultAllocator() ) local E @@ -230,7 +229,7 @@ function _localupdate_sweep_idmrg!( end function _localupdate_sweep_idmrg2!( - ψ, H, envs, alg_eigsolve, alg_trunc, alg_svd, timeroutput::TimerOutput; + ψ, H, envs, alg_eigsolve, alg_trunc, alg_svd, timeroutput; backend::AbstractBackend = DefaultBackend(), allocator = DefaultAllocator() ) # @timeit wraps its body in try-finally, which is a new lexical scope: declare locals diff --git a/src/algorithms/groundstate/vumps.jl b/src/algorithms/groundstate/vumps.jl index 716d2e21d..7be8c096a 100644 --- a/src/algorithms/groundstate/vumps.jl +++ b/src/algorithms/groundstate/vumps.jl @@ -43,14 +43,14 @@ Used as the `algorithm` argument of [`find_groundstate`](@ref) and [`leading_bou end # Internal state of the VUMPS algorithm -struct VUMPSState{S, O, E} +struct VUMPSState{S, O, E, T} mps::S operator::O envs::E iter::Int ϵ::Float64 which::Symbol - timeroutput::TimerOutput + timeroutput::T end function find_groundstate( @@ -64,8 +64,7 @@ function dominant_eigsolve( which ) log = IterLog("VUMPS") - timeroutput = TimerOutput("VUMPS") - alg.verbosity > 3 || disable_timer!(timeroutput) + timeroutput = alg.verbosity > 3 ? TimerOutput("VUMPS") : NoTimerOutput() iter = 0 mps = copy(mps) @@ -81,12 +80,12 @@ function dominant_eigsolve( for (mps, envs, ϵ) in it if ϵ ≤ alg.tol - @infov 4 timeroutput + @infov 4 TimerReport(timeroutput) @infov 2 logfinish!(log, it.iter, ϵ, expectation_value(mps, operator, envs)) return mps, envs, ϵ end if it.iter ≥ alg.maxiter - @infov 4 timeroutput + @infov 4 TimerReport(timeroutput) @warnv 1 logcancel!(log, it.iter, ϵ, expectation_value(mps, operator, envs)) return mps, envs, ϵ end @@ -137,17 +136,16 @@ function localupdate_step!( ACs = mps.AL dst_ACs = mps isa Multiline ? eachcol(ACs) : ACs - tree_point = String[section.name for section in state.timeroutput.timer_stack] + tree_point = timer_treepoint(state.timeroutput) allocator = default_allocator(mps, scheduler) tforeach(eachsite(mps); scheduler) do site - sub_timeroutput = TimerOutput() + sub_timeroutput = subtimer(state.timeroutput) dst_ACs[site] = _localupdate_vumps_step!( site, mps, state.operator, state.envs, src_ACs[site], src_Cs[site]; alg_orth, state.which, alg_eigsolve, timeroutput = sub_timeroutput, it.backend, allocator, ) - state.timeroutput.enabled && - merge!(state.timeroutput, sub_timeroutput; tree_point) + merge_subtimer!(state.timeroutput, sub_timeroutput; tree_point) end return ACs @@ -157,7 +155,7 @@ function _localupdate_vumps_step!( site, mps, operator, envs, AC₀, C₀; alg_orth = Defaults.alg_orth(), alg_eigsolve = Defaults.eigsolver, which, - timeroutput::TimerOutput = DISABLED_TIMER, + timeroutput = NoTimerOutput(), backend::AbstractBackend = DefaultBackend(), allocator = DefaultAllocator(), ) local AC, C diff --git a/src/algorithms/timestep/bug.jl b/src/algorithms/timestep/bug.jl index 1ea9edd4d..00916ab90 100644 --- a/src/algorithms/timestep/bug.jl +++ b/src/algorithms/timestep/bug.jl @@ -163,7 +163,7 @@ function timestep!( ψ::AbstractFiniteMPS, H, t::Number, dt::Number, alg::BUG, envs::AbstractMPSEnvironments = environments(ψ, H, ψ); imaginary_evolution::Bool = false, normalize::Bool = false, - timeroutput::TimerOutput = DISABLED_TIMER + timeroutput = NoTimerOutput() ) L = length(ψ) h = dt / 2 diff --git a/src/environments/infinite_envs.jl b/src/environments/infinite_envs.jl index 25285068e..2a57a10ea 100644 --- a/src/environments/infinite_envs.jl +++ b/src/environments/infinite_envs.jl @@ -20,7 +20,7 @@ rightenv(envs::InfiniteEnvironments, site::Int, state) = envs.GRs[site] function environments( below::InfiniteMPS, operator::Union{InfiniteMPO, InfiniteMPOHamiltonian}, above; - timeroutput::TimerOutput = DISABLED_TIMER, kwargs... + timeroutput = NoTimerOutput(), kwargs... ) alg = environment_alg(below, operator, above; kwargs...) return environments(below, operator, above, alg; timeroutput) @@ -28,7 +28,7 @@ end function environments( below::InfiniteMPS, operator::Union{InfiniteMPO, InfiniteMPOHamiltonian}, above, alg; - timeroutput::TimerOutput = DISABLED_TIMER + timeroutput = NoTimerOutput() ) GLs, GRs = initialize_environments(below, operator, above) envs = InfiniteEnvironments(GLs, GRs) @@ -58,7 +58,7 @@ end function recalculate!( envs::InfiniteEnvironments, below, operator::Union{InfiniteMPO, InfiniteMPOHamiltonian}, above = below; - timeroutput::TimerOutput = DISABLED_TIMER, kwargs... + timeroutput = NoTimerOutput(), kwargs... ) alg = environment_alg(below, operator, above; kwargs...) return recalculate!(envs, below, operator, above, alg; timeroutput) @@ -84,7 +84,7 @@ function recalculate!( envs::InfiniteEnvironments, below::InfiniteMPS, operator::Union{InfiniteMPO, InfiniteMPOHamiltonian}, above::InfiniteMPS, alg; - timeroutput::TimerOutput = DISABLED_TIMER, + timeroutput = NoTimerOutput(), ) if !issamespace(envs, below, operator, above) # TODO: in-place initialization? @@ -93,17 +93,17 @@ function recalculate!( copy!(envs.GRs, GRs) end - tree_point = String[section.name for section in timeroutput.timer_stack] + tree_point = timer_treepoint(timeroutput) @sync begin @spawn begin - sub_timeroutput = TimerOutput() + sub_timeroutput = subtimer(timeroutput) @timeit sub_timeroutput "left_envs" compute_leftenvs!(envs, below, operator, above, alg) - timeroutput.enabled && merge!(timeroutput, sub_timeroutput; tree_point) + merge_subtimer!(timeroutput, sub_timeroutput; tree_point) end @spawn begin - sub_timeroutput = TimerOutput() + sub_timeroutput = subtimer(timeroutput) @timeit sub_timeroutput "right_envs" compute_rightenvs!(envs, below, operator, above, alg) - timeroutput.enabled && merge!(timeroutput, sub_timeroutput; tree_point) + merge_subtimer!(timeroutput, sub_timeroutput; tree_point) end end normalize!(envs, below, operator, above) diff --git a/src/states/ortho.jl b/src/states/ortho.jl index 9dfc803e4..68b062d35 100644 --- a/src/states/ortho.jl +++ b/src/states/ortho.jl @@ -112,7 +112,7 @@ gaugefix! function gaugefix!( ψ::InfiniteMPS, A, C₀ = ψ.C[end]; - order = :LR, timeroutput::TimerOutput = DISABLED_TIMER, kwargs... + order = :LR, timeroutput = NoTimerOutput(), kwargs... ) alg = if order === :LR || order === :RL MixedCanonical(; order, kwargs...) @@ -130,7 +130,7 @@ end # expert mode: actual implementation function gaugefix!( ψ::InfiniteMPS, A, C₀, alg::MixedCanonical; - timeroutput::TimerOutput = DISABLED_TIMER + timeroutput = NoTimerOutput() ) if alg.order === :LR gaugefix!(ψ, A, C₀, alg.alg_leftcanonical; timeroutput) @@ -145,14 +145,14 @@ function gaugefix!( end function gaugefix!( ψ::InfiniteMPS, A, C₀, alg::LeftCanonical; - timeroutput::TimerOutput = DISABLED_TIMER + timeroutput = NoTimerOutput() ) uniform_leftorth!((ψ.AL, ψ.C), A, C₀, alg; timeroutput) return ψ end function gaugefix!( ψ::InfiniteMPS, A, C₀, alg::RightCanonical; - timeroutput::TimerOutput = DISABLED_TIMER + timeroutput = NoTimerOutput() ) uniform_rightorth!((ψ.AR, ψ.C), A, C₀, alg; timeroutput) return ψ @@ -214,7 +214,7 @@ end function uniform_leftorth!( (AL, C), A, C₀, alg::LeftCanonical; - timeroutput::TimerOutput = DISABLED_TIMER + timeroutput = NoTimerOutput() ) C[end] = normalize!(C₀) return LoggingExtras.withlevel(; alg.verbosity) do @@ -279,7 +279,7 @@ end function uniform_rightorth!( (AR, C), A, C₀, alg::RightCanonical; - timeroutput::TimerOutput = DISABLED_TIMER + timeroutput = NoTimerOutput() ) C[end] = normalize!(C₀) return LoggingExtras.withlevel(; alg.verbosity) do diff --git a/test/Project.toml b/test/Project.toml index ea2afba0a..5597b8c84 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -32,7 +32,7 @@ Combinatorics = "1" ParallelTestRunner = "2" Plots = "1.40" Pkg = "1" -TensorKitTensors = "0.2" +TensorKitTensors = "0.2, 0.3" Test = "1" TestExtras = "0.3" cuTENSOR = "6" diff --git a/test/runtests.jl b/test/runtests.jl index 64e3e6509..ef29c29ad 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,7 +23,7 @@ using CUDA, cuTENSOR using AMDGPU AMDGPU.functional() || - filter!(!(startswith("gpu/amd") ∘ first), testsuite) + filter!(!(startswith("gpu/rocm") ∘ first), testsuite) # parse arguments