Contracts across threads: a full fund in two minutes on a desktop

1 September 2026 · measurement

The finding

Concurrency at the contract level changes what a machine can do. In uModel the unit of parallel work is the individual contract, so a projection spreads across every available thread without being reorganised to allow it.

This architecture is expected to significantly reduce cloud cost after it is deployed.

Most projection systems parallelise at a coarser grain. Work is distributed across scenarios, or across groups of contracts assembled for the purpose — a partition the modeller has to define, maintain, and reason about. That works, but it ties the available concurrency to how the run was carved up rather than to how much hardware is present: a single-scenario run has nothing to spread, and an awkward grouping leaves threads idle. Getting finer-grained concurrency out of such a system usually means restructuring the model.

Here the contract is already an independent unit of work, so there is no partition to design. Seven scenarios or one, ten thousand contracts or a hundred thousand — the same run fills the machine.

That is an architectural property, not a tuning setting. The measurement below is what it looks like on one desktop.

The run

One fund. Three contract types projected together — liabilities and assets advancing in the same run, not reconciled afterwards.

ContractsWork per contract
Term life9,995Monthly cash flows, an iterative Newton solve for net level premium at issue, a reserve at every year
Universal life5,000Account value mechanics — cost of insurance, loads, credited interest, cash value — plus solves for guaranteed maturity premium and guaranteed maturity fund
Fixed-rate bonds1,000Cash flows, and credit rating migration through an eight-state model — AAA to CCC plus Default, driven by a transition matrix

Roughly 16,000 contracts, seriatim — no compression, no grouping, no model points. The solving is per contract, not per model point.

7 scenarios. 600 months — 50 years. Every contract, every month, every scenario.

One desktop: eight cores, sixteen logical processors.

The result

2 minutes 13 seconds. 132,677 ms from enqueue to completion, all seven scenarios finished.

The engine measures its own thread engagement: wall time against CPU time consumed gives the number of threads actually kept busy.

ContractsWallCPUThreads busy (of 16)
Term life9,995258 ms3,656 ms14.2
Universal life5,000152 ms2,046 ms13.5
Fixed-rate bonds1,000115 ms859 ms7.5

Windows Task Manager during the run: all sixteen logical processors driven to near 100%, with brief dips where one scenario ends and the next begins

All sixteen logical processors during this run. Each dip is a scenario boundary: one scenario finishes and the next is set up before contract work resumes.

With seven scenarios completing inside a couple of minutes, those boundaries fall often enough to show. Run the same model over a hundred thousand contracts and each scenario takes long enough that a sixty-second window may contain no boundary at all — the same view becomes a flat wall:

Windows Task Manager during a 100,000-contract stress test: all sixteen logical processors held flat at 100% with no visible dips

The same machine during a 100,000-contract stress test on 2026-08-30. No dips in the window — at that volume a single scenario occupies the processor for longer than the chart shows.

Two caveats worth stating plainly. Sixteen logical processors is eight physical cores with hyperthreading, so fourteen threads busy is not a fourteen-fold speed-up. And this run recomputes term cash flows for all seven scenarios even though they do not vary by scenario — reuse is an optimisation not applied here, so the time is an upper bound.

None of that changes the shape of the result. One desktop carried sixteen thousand seriatim contracts with per-contract solving, an asset portfolio under credit migration, and a full scenario set, in a little over two minutes — because the work was already in a form the hardware could take.