Performance · reproducible

How fast is it, really?

Every performance claim on a software site is a number you are asked to take on faith. This one is not: the benchmark is a mode of the app itself, running the real engine on a fixed model at a fixed seed, in your browser, on your machine. Click through and check us.

01Our measurement

Built-in demo programme — 17 activities, 5 register risks, full forward and backward CPM pass per iteration with sampled durations, discrete risk events and weather resolved. Seed 8812. Measured in headless Chromium 1194 on a cloud container, single-threaded:

IterationsWall clockPer iterationIterations/secP80 result
2,000574 ms0.287 ms3,484349.26 d
5,0001,172 ms0.234 ms4,266348.18 d
10,0001,955 ms0.196 ms5,115348.18 d

Peak throughput 5,115 iterations/sec on one core. A 10,000-iteration QSRA of this programme completes in about two seconds. Per-iteration cost falls as the run grows because the fixed setup — graph build, topological order, range resolution — is amortised over more iterations.

Why the P80 column is thereAn engine that is fast and wrong is not fast. Printing the answer each run produced makes the benchmark self-checking, and it shows something useful in its own right: the P80 moves from 349.26 at 2,000 iterations to 348.18 at 5,000 and stays there at 10,000. That is the convergence diagnostic doing its job — and the honest reason the free tier's 2,000 iterations are enough to make a decision but not to quote a date to three decimal places.

Your numbers will differ — and that is the point. Hardware, browser and background load all matter. Run it on the machine you would actually use.

02One core, on purpose — for now

The engine runs on the main thread, in chunks, so the interface stays responsive and the progress bar is truthful. It does not yet use Web Workers or multiple cores. We would rather say that plainly than let a benchmark imply otherwise.

Moving to a parallel engine is not simply a matter of spawning workers, and the reason is worth stating because it is the same principle as everything else here. Iterations currently share one seeded random stream: iteration 4,000 draws the numbers it does because of every iteration before it. Splitting that stream across cores means seeding each iteration independently, which produces a different — equally valid, equally seeded — set of numbers. Every existing report and reproducibility certificate would stop matching a fresh run of the same file.

That is a deliberate trade, not an accident, and it belongs to the people relying on those certificates rather than to us. When parallelism ships it will carry its own engine version string, printed on every report, so a reader can always tell which engine produced a number. Reproducibility is the product; speed is a feature.

What "fast enough" already means2,000 iterations in about half a second and 10,000 in two seconds means the slow part of a QSRA is not the simulation — it is agreeing the ranges. We would rather spend engineering effort on the elicitation workflow than on shaving a second off a two-second run.

03Reproduce it

Open /app?benchmark=1 and press Run. The mode loads the same demo programme, runs the same three iteration counts at seed 8812, and prints the same five columns. No account, no upload, no server: the page you are measuring is the page you are running.

To benchmark your own programme instead, load it in the app, set the seed, and time a run — the reproducibility block on every report already prints the seed, iteration count and engine version needed to repeat it exactly.

Method Wall clock is measured with performance.now() around the full scenario, including sampling, CPM, risk resolution and result reduction — not the inner loop alone.