FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
Performance tuning

This page is a decision-oriented guide to making simulations run faster. It does not repeat the full option reference — for exact attributes and defaults see SIMULATION section, and for analytical Jacobians see Implicit Integration with Analytical Jacobians.

Before tuning, first confirm where the time goes. Enable diagnostics (see Diagnostics cookbook) to see whether the step size is collapsing, whether the Jacobian is being rebuilt often, or whether a particular state is forcing small steps.

1. Match the integrator to the problem

  • Non-stiff / smooth dynamics — an explicit method (e.g. RK45_i) with sensible tolerances is usually fastest.
  • Stiff dynamics (fast and slow modes together, tight coupling, stiff springs) — an implicit method (BDF, DIRK) avoids the tiny steps explicit methods need.

For the full method list (canonical names, order, explicit/implicit, Engine vs Sundials backend) and a "which method when" table, see the method reference in SIMULATION section.

Set tolerances no tighter than your accuracy requirement: over-tight AbsTol/RelTol in <StepControl> forces small steps and more work.

2. Choose the linear solver (implicit methods only)

The linear-solver choice dominates cost for large implicit systems. There are two disjoint vocabularies, and which one applies depends on the integrator backend:

  • Sundials methods (BDF, DIRK) — <LinearSolver Type="..."> takes only SPGMR, DENSE or BAND. The structured, model-side solvers below are then selected with <LinearSolver Preconditioner="...">, which is accepted for Type="SPGMR" only; a direct DENSE/BAND solver takes no preconditioner.
  • Engine methods (the _i family, e.g. BackwardEuler_i) — <LinearSolver Type="..."> takes the structured names below directly.

So the rules of thumb in this table name the value of Preconditioner for a Sundials run and of Type for an Engine run (full reference in SIMULATION section):

System Suggested structured solver
Small (< ~100 states) dense
Large and sparse sparse (METIS-ordered)
Chain-coupled (cables, moorings) band, block_tridiagonal, or near_tridiagonal
Many weakly-coupled blocks component
Very large, iterative convergence acceptable iterative / iterative_gmres
Not sure auto (lets FhSim decide from structure and size)

A typical stiff, large, sparse configuration therefore reads:

<Integrator Method="BDF">
<LinearSolver Type="SPGMR" Preconditioner="sparse"/>
</Integrator>
Note
On an explicit Engine method (RK45_i and friends) a <LinearSolver> element is irrelevant and is ignored with a warning.

3. Exploit sparsity

For large models, a sparse Jacobian avoids dense factorisation cost. Set <Jacobian Type="sparse"/>; to also visualise the coupling, add SparsityImageSize to the same element — <Jacobian Type="sparse" SparsityImageSize="512"/> — and check the structure is as expected (a dense-looking pattern means little is gained from a sparse solver). Only the first <Jacobian> element is read, so keep it to one.

Providing an analytical Jacobian (rather than the numerical default) is often the single biggest win for stiff systems — see Implicit Integration with Analytical Jacobians.

4. Use multiple cores

<Integrator NumCores="..."> parallelises SimObject evaluation:

Value Meaning
1 Single-threaded (default).
perCpuCore One thread per CPU core.
perSimObject One thread per state-carrying SimObject.
N At most N threads.

The engine caps the thread count at the machine's hardware threads and at the number of SimObjects, and logs (at Info) when it does. It then times the first serial evaluations, SimObject by SimObject, and tries parallel evaluation only if both

  • the median serial evaluation takes at least 250 µs, and
  • parallel evaluation could be at least 1.5 times faster: no parallel evaluation beats the most expensive SimObject, nor the serial time divided by the threads.

Otherwise it stays serial and starts no worker thread. If it tries, it times parallel evaluations too and keeps the faster. The choice is logged at Info ("Model evaluation: median ... us serial, largest SimObject ..."). Both ways compute the same values: the output does not depend on NumCores.

<Integrator ParallelEvaluation="..."> overrides the choice:

Value Meaning
auto As above (default).
always Evaluate in parallel from the first evaluation, without timing, whenever NumCores > 1 and the model has at least two SimObjects. For models whose expensive SimObjects do not wait for each other, such as several independent net cages, that the 250 µs threshold keeps serial.
never Evaluate serially; no thread is ever started, whatever NumCores says.

The output is byte-identical in all three modes and for any NumCores.

While a model is evaluated in parallel, each idle worker thread spins for up to 1 ms waiting for the next evaluation before it sleeps, so a running parallel model keeps its cores busy. Waking a sleeping thread costs tens of microseconds; with a shorter spin the workers fell asleep between evaluations and parallel evaluation was 2–4 times slower.

Parallelism pays off when several SimObjects are expensive and do not wait for each other: eight independent 128-panel net cages (280 µs per evaluation) run about 4 times faster on 8 cores. Trawl models do not gain: their cables and nets read each other's ports and common computations, so in parallel they mostly wait (2–10 times slower per evaluation), and they evaluate in 40–110 µs, below the threshold. The threshold matters because a process that has once started a second thread runs single-threaded code slower for the rest of the run (12–15 % on a trawl), even after that thread has stopped.

‍Thread-safety note. With NumCores > 1, SimObject OdeFcn/port evaluations run concurrently. Shared computations must be registered as a common computation so the engine serialises and caches them (it wraps them in a mutex); do not share mutable state between SimObjects through other means.

5. Reduce output overhead

Writing state/observer output every step is costly. Increase the output interval (dt / TOutput) and limit observers to what you actually need (see OBSERVERS section).

Profiling

FhSim 3 has no built-in CPU profiler. Use an external profiler (perf, VTune, Instruments) for hot-spot analysis, and the diagnostics JSON/HTML viewer (Diagnostics cookbook) to understand solver behaviour over time.

See also