You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tracking issue for performance work on continuous-time reservoirs (ContinuousESN / SciMLProblemReservoir / RCODEReservoirExt), corresponding to PR4 of the SciML fellowship plan on #397.
PR1–PR3 landed the correct continuous core (#446, #450, #456). This issue tracks data-driven optimizations so each change can land as a small, reviewable PR with a before/after measurement.
Profiling-only harness (no package code changes): benchmarks/continuous_profile/ (local for now; can be proposed separately if useful upstream).
Motivation
On Apple M4 / Julia 1.12.5 / single-thread OpenBLAS, with reltol=1e-6, abstol=1e-8, Float64, use_bias=true, seed 17:
Workload
Setup
Median
W3
ContinuousESNcollectstates, N=500, T=1000, Tsit5
~13.5 s
W4
same, Eulerdt=1
~42 ms
W5 / W5t
Rodas5 vs Tsit5, N=100, T=200
~1.57 s / ~0.12 s
W7
AR predict, N=500, 250 steps, Tsit5
~0.76 s
W10b
discrete ESNcollectstates, N=500, T=1000
~42 ms
Key ratios:
Tsit5 / Euler (matched N=500, T=1000): ~320×
Continuous Tsit5 / discrete ESN (N=500): ~350×
Rodas5 / Tsit5 (matched N=100, T=200): ~13×
Stage timers show solve ≈ 100% of wall time on every continuous path. Grid construction, ZOH, NamedTuple merges, and modifiers are negligible once warm.
Profiling findings (granular)
1. Adaptive stepping dominates, not the RHS wrapper
Default RHS _continuous_esn_rhs! is already 0 allocations / call (mul! + in-place broadcast). Re-optimizing matvec alone will not hit a 3× target if NFE counts stay the same.
2. Teacher-forced path is one long solve
collectstates builds a ZOH input, one ODEProblem, then solve(...; saveat=sample_ts). Flamegraphs for W3 land in:
OrdinaryDiffEqTsit5perform_step!
residual / error-control helpers
tanh / FastBroadcast
mul! (matvec) at larger N
3. Autoregressive path rebuilds the problem every step
W7: 250 independent short solves. Per-step costs (alloc audit):
Stage
Bytes / step
solve
~76 KB
ODEProblem build
~3 KB
ConstantInterpolation input
~0.7 KB
Profile.Allocs is dominated by fresh ODEProblem / ODEIntegrator / ConstantInterpolation types each step — classic integrator reuse surface (init once + reinit!).
4. Implicit solvers without sparsity are expensive
W5 Rodas5 reports 2978 Jacobians (njacs) and ~334k RHS calls at N=100, T=200, with dense finite-difference Jacobian cost. Sparse jac_prototype from rand_sparse is the natural fix.
5. Allocation audit (secondary to wall time)
Site
Notes
reduce(hcat, sol.u) in _sample
~half of oneshot bytes on W3; wall time tiny when warm
solve solution / integrator buffers
other half on W3
AR per-step rebuild
main AR allocation driver
ZOH returns a true SubArray view (no vector copy of the input column).
Summary
Tracking issue for performance work on continuous-time reservoirs (
ContinuousESN/SciMLProblemReservoir/RCODEReservoirExt), corresponding to PR4 of the SciML fellowship plan on #397.PR1–PR3 landed the correct continuous core (#446, #450, #456). This issue tracks data-driven optimizations so each change can land as a small, reviewable PR with a before/after measurement.
Profiling-only harness (no package code changes):
benchmarks/continuous_profile/(local for now; can be proposed separately if useful upstream).Motivation
On Apple M4 / Julia 1.12.5 / single-thread OpenBLAS, with
reltol=1e-6,abstol=1e-8, Float64,use_bias=true, seed 17:ContinuousESNcollectstates, N=500, T=1000, Tsit5dt=1predict, N=500, 250 steps, Tsit5ESNcollectstates, N=500, T=1000Key ratios:
Stage timers show
solve≈ 100% of wall time on every continuous path. Grid construction, ZOH, NamedTuple merges, and modifiers are negligible once warm.Profiling findings (granular)
1. Adaptive stepping dominates, not the RHS wrapper
nf), 21 262 accepted steps, 33 827 rejected._continuous_esn_rhs!is already 0 allocations / call (mul!+ in-place broadcast). Re-optimizing matvec alone will not hit a 3× target if NFE counts stay the same.2. Teacher-forced path is one long
solvecollectstatesbuilds a ZOH input, oneODEProblem, thensolve(...; saveat=sample_ts). Flamegraphs for W3 land in:OrdinaryDiffEqTsit5perform_step!tanh/ FastBroadcastmul!(matvec) at larger N3. Autoregressive path rebuilds the problem every step
W7: 250 independent short solves. Per-step costs (alloc audit):
solveODEProblembuildConstantInterpolationinputProfile.Allocs is dominated by fresh
ODEProblem/ODEIntegrator/ConstantInterpolationtypes each step — classic integrator reuse surface (initonce +reinit!).4. Implicit solvers without sparsity are expensive
W5 Rodas5 reports 2978 Jacobians (
njacs) and ~334k RHS calls at N=100, T=200, with dense finite-difference Jacobian cost. Sparsejac_prototypefromrand_sparseis the natural fix.5. Allocation audit (secondary to wall time)
reduce(hcat, sol.u)in_samplesolvesolution / integrator buffersZOH returns a true
SubArrayview (no vector copy of the input column).6. Train is collectstates-bound
W8
train!: 99.8% incollectstates; ridge ~18 ms.Non-goals (this issue)
predictwarmup / cold-start (deferred separately on feat(models): ContinuousESN #456 discussion).Project.tomlfree of BenchmarkTools etc.).References
ContinuousESNmodel: feat(models): ContinuousESN #456benchmarks/continuous_profile/results/REPORT.mdEnvironment used for numbers above
-O3 --check-bounds=no2f47eeb5a4b3876c2f72b032217434fc84aa007ccc @MartinuzziFrancesco