Significant Difference in Runtime Between Specified and Unspecified Noise

There seems to be a significant difference in runtime when the noise process is specified. In the simple case of:

using DifferentialEquations
using BenchmarkTools

global T = 10.0::Float64

f(du, u, p, t) = du .= 0.0+0.0im
g(du, u, p, t) = du .= 1.0+0.0im

prob = SDEProblem(f, g, zeros(ComplexF64,5), (0.0, T))

function func(prob)
    return solve(prob, EM(), dt = 1e-4, saveat = 1e-3)
end

We have a 5D stochastic function that only depends on the noise term. Benchmarking the function, we see that:

@benchmark func(p) setup=(p=$prob) seconds=20.0 samples=20000 evals=1
BenchmarkTools.Trial: 1568 samples with 1 evaluation.
 Range (min … max):  12.135 ms …  18.741 ms  ┊ GC (min … max): 0.00% … 31.25%
 Time  (median):     12.658 ms               ┊ GC (median):    0.00%
 Time  (mean ± σ):   12.727 ms ± 378.553 μs  ┊ GC (mean ± σ):  0.35% ±  1.73%

Then, changing the code by adding the following line and changing the defined SDE problem via:

W = WienerProcess(0.0,zeros(ComplexF64,5))
prob = SDEProblem(f, g, zeros(ComplexF64,5), (0.0, T), noise = W)

My understanding is that the Wiener process defined here ought to be the same as the automatically selected one. (I have checked and the automatically selected noise is complex as well.) However, benchmarking this function gives:

@benchmark func(p) setup=(p=$prob) seconds=20.0 samples=20000 evals=1
BenchmarkTools.Trial: 148 samples with 1 evaluation.
 Range (min … max):   61.358 ms … 350.173 ms  ┊ GC (min … max): 15.66% … 82.36%
 Time  (median):      84.430 ms               ┊ GC (median):    42.11%
 Time  (mean ± σ):   134.533 ms ±  95.967 ms  ┊ GC (mean ± σ):  60.11% ± 22.32%

Here, the runtime is more than 10 times longer. As such, I was wondering if anyone had any insight into where this difference in runtime comes from, and if it is possible to bring specified noise processes in line with automatically selected noises.

By default it uses SimpleWienerProcess that optimizes a bit more.

Changing the noise type to

W = SimpleWienerProcess!(0.0,zeros(ComplexF64,5))

gives:

@benchmark func(p) setup=(p=$prob) seconds=20.0 samples=20000 evals=1
BenchmarkTools.Trial: 36 samples with 1 evaluation.
 Range (min … max):   37.388 ms …    1.656 s  ┊ GC (min … max):  0.00% … 75.49%
 Time  (median):     429.430 ms               ┊ GC (median):    42.14%
 Time  (mean ± σ):   547.136 ms ± 428.562 ms  ┊ GC (mean ± σ):  52.77% ± 24.12%

which is worse than the WienerProcess. Meanwhile

W = SimpleWienerProcess(0.0,zeros(ComplexF64,5))

throws the following error:

ERROR: MethodError: no method matching WHITE_NOISE_DIST(::SimpleNoiseProcess{…}, ::Float64, ::Vector{…}, ::SciMLBase.NullParameters, ::Float64, ::RandomNumbers.Xorshifts.Xoroshiro128Plus)
An arithmetic operation was performed on a NullParameters object. This means no parameters were passed into the AbstractSciMLProblem (e.x.: ODEProblem) but the parameters object `p` was used in an arithmetic expression.

On the other hand,

WienerProcess!(0.0,zeros(ComplexF64,5))

gives:

@BenchmarkTools.Trial: 527 samples with 1 evaluation.
 Range (min … max):  23.701 ms … 121.852 ms  ┊ GC (min … max):  0.00% … 72.00%
 Time  (median):     33.316 ms               ┊ GC (median):     0.00%
 Time  (mean ± σ):   37.857 ms ±  18.365 ms  ┊ GC (mean ± σ):  12.99% ± 17.70%


Unfortunately I don’t understand why these are the case.

In-place will reuse memory and thus limit the number of allocation operations.

I see, thank you. In that case:

  1. Why does SimpleWienerProcess! take longer than WienerProcess! (and WienerProcess)?
  2. Is there a reason not to use in-place?
  3. Back to the original question, in all cases a specified noise was slower than the automatic one. Is there a way of improving the performance of specified noises?

I appreciate your patience btw, I’m just trying to understand all of this.

StaticArrays

I’d have to investigate that deeper.

If you share a profile I could help point out what can be done.