Skip to content

Commit 675d990

Browse files
Add callback that estimates walltime
1 parent b26b78b commit 675d990

File tree

5 files changed

+96
-2
lines changed

5 files changed

+96
-2
lines changed

src/cache/cache.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
struct AtmosCache{
22
FT <: AbstractFloat,
3+
FTE,
4+
WTE,
35
SD,
46
AM,
57
NUM,
@@ -28,6 +30,12 @@ struct AtmosCache{
2830
"""Timestep of the simulation (in seconds). This is also used by callbacks and tendencies"""
2931
dt::FT
3032

33+
"""Timestep of the simulation (in seconds). This is also used by callbacks and tendencies"""
34+
t_end::FTE
35+
36+
"""Walltime estimate"""
37+
walltime_estimate::WTE
38+
3139
"""Start date (used for insolation)."""
3240
start_date::SD
3341

@@ -92,7 +100,7 @@ end
92100

93101
# The model also depends on f_plane_coriolis_frequency(params)
94102
# This is a constant Coriolis frequency that is only used if space is flat
95-
function build_cache(Y, atmos, params, surface_setup, dt, start_date)
103+
function build_cache(Y, atmos, params, surface_setup, dt, t_end, start_date)
96104
FT = eltype(params)
97105

98106
ᶜcoord = Fields.local_geometry_field(Y.c).coordinates
@@ -183,6 +191,8 @@ function build_cache(Y, atmos, params, surface_setup, dt, start_date)
183191

184192
args = (
185193
dt,
194+
t_end,
195+
WallTimeEstimate(),
186196
start_date,
187197
atmos,
188198
numerics,

src/callbacks/callbacks.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,47 @@ function save_restart_func(integrator, output_dir)
449449
return nothing
450450
end
451451

452+
Base.@kwdef mutable struct WallTimeEstimate
453+
n_steps::Int = 0
454+
n_next::Int = 1
455+
t_last::Float64 = -1
456+
∑Δt_wall::Float64 = 0
457+
end
458+
import Dates
459+
function print_walltime_estimate(integrator)
460+
(; walltime_estimate, dt, t_end) = integrator.p
461+
t = integrator.t
462+
wte = walltime_estimate
463+
if wte.n_steps > 1 # 1 to avoid call during initialization
464+
t_wall_ave_per_step = wte.∑Δt_wall / wte.n_steps
465+
twps = t_wall_ave_per_step
466+
percent_complete = round(t / t_end * 100; digits = 1)
467+
n_steps = ceil(Int, t_end / dt)
468+
n_steps_remaining = n_steps - wte.n_steps
469+
erwt = twps * n_steps_remaining
470+
ewtt = twps * n_steps
471+
spent = wte.∑Δt_wall
472+
pwu =
473+
x -> trunc_time(
474+
string(
475+
Dates.canonicalize(Dates.CompoundPeriod(x, Dates.Second)),
476+
),
477+
)
478+
if wte.n_steps == wte.n_next
479+
@info "Wall time" per_step = pwu(twps) total = pwu(erwt) remaining =
480+
pwu(ewtt) spent = pwu(spent) percent_complete = percent_complete
481+
wte.n_next *= 2 # doubling factor (to reduce log noise)
482+
end
483+
Δt = time() - wte.t_last
484+
else
485+
Δt = Float64(0)
486+
end
487+
wte.∑Δt_wall += Δt
488+
wte.n_steps += 1
489+
wte.t_last = time()
490+
return nothing
491+
end
492+
452493
function gc_func(integrator)
453494
full = true # whether to do a full GC
454495
num_pre = Base.gc_num()

src/solver/type_getters.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,12 @@ function get_callbacks(parsed_args, sim_info, atmos, params, comms_ctx)
455455
FT = eltype(params)
456456
(; dt, output_dir) = sim_info
457457

458-
callbacks = ()
458+
callbacks = (
459+
call_every_n_steps(
460+
(integrator) -> print_walltime_estimate(integrator);
461+
skip_first = true,
462+
),
463+
)
459464
dt_save_to_disk = time_to_seconds(parsed_args["dt_save_to_disk"])
460465
if !(dt_save_to_disk == Inf)
461466
callbacks = (
@@ -775,6 +780,7 @@ function get_simulation(config::AtmosConfig)
775780
params,
776781
surface_setup,
777782
sim_info.dt,
783+
sim_info.t_end,
778784
sim_info.start_date,
779785
)
780786
end

src/utils/utilities.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,42 @@ function prettytime(t)
210210
return @sprintf("%.3f %s", value, units)
211211
end
212212

213+
import Dates
214+
using Dates
215+
216+
time_per_time(::Type{P}, ::Type{P}) where {P} = 1
217+
function define_time_per_times(periods)
218+
for i in eachindex(periods)
219+
T, n = periods[i]
220+
N = Int64(1)
221+
for j in (i - 1):-1:firstindex(periods) # less-precise periods
222+
Tc, nc = periods[j]
223+
N *= nc
224+
@eval time_per_time(::Type{$T}, ::Type{$Tc}) = $N
225+
end
226+
end
227+
end
228+
229+
# # From Dates
230+
define_time_per_times([
231+
(:Week, 7),
232+
(:Day, 24),
233+
(:Hour, 60),
234+
(:Minute, 60),
235+
(:Second, 1000),
236+
(:Millisecond, 1000),
237+
(:Microsecond, 1000),
238+
(:Nanosecond, 1),
239+
])
240+
241+
function Dates.CompoundPeriod(x::Real, ::Type{T}) where {T <: Period}
242+
nf = time_per_time(Nanosecond, T)
243+
return Dates.canonicalize(Dates.CompoundPeriod(Nanosecond(ceil(x * nf))))
244+
end
245+
246+
trunc_time(s::String) = count(',', s) > 1 ? join(split(s, ",")[1:2], ",") : s
247+
248+
213249
function prettymemory(b)
214250
if b < 1024
215251
return string(b, " bytes")

test/coupler_compatibility.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const T2 = 290
5858
@. sfc_setup = (surface_state,)
5959
p_overwritten = CA.AtmosCache(
6060
p.dt,
61+
WallTimeEstimate(),
6162
p.start_date,
6263
p.atmos,
6364
p.numerics,

0 commit comments

Comments
 (0)