Backtest profile

Where the backtest spends its CPU. The hourly evaluation and the walk-forward replay run under callgrind, which counts every instruction executed, so the figures are exact and repeat run to run. Code the compiler inlined is put back under the function it came from. See how it is made.

Loading…

Flame graph

Open full size

Each box is a function, as wide as its share of instructions, stacked on its caller. Click a box to zoom into it; search with Ctrl-F inside the full-size view.

Phases

What it found, and what changed, 28 October

The first profile found two hot spots. Both are fixed, with the trades unchanged: every pair's evaluation and trades on all 101 walked days came out identical before and after. The walk now takes 7.6 seconds instead of 52, and the same work runs on five times fewer instructions.

  1. The New York offset was 57% of the backtest. time::ny_offset worked out the year's daylight-saving dates from scratch each time it was asked, and morning_breakout asked for each of the last 100 bars on every bar it tested. The dates for 1970 to 2100 are now a table built when the crate compiles, morning_breakout takes one offset for the whole session, and the market windows convert a bar's time once instead of four times.
  2. Bars were validated again on every look: 18%. entry::tail checked Bar::is_valid on the whole window each time a strategy looked, 108 bars for macd_crossover. Bars are now checked once, where they come in: series already kept only valid bars, and the snapshot now drops any others.

What is left is spread out. The backtest loop itself is about half, macd_crossover and morning_breakout are 18% each (MACD recomputes its averages over 108 bars on every bar), and the other strategies are 3% or less. The walk-forward replay is still nearly all of it, as it evaluates every pair afresh on the 60-day window before each walked day.

Measured on the synthetic market. A real history changes how often strategies signal, not where a bar's checks spend their time.

Hottest functions

Self: instructions in the function's own code. Total: with everything it calls, inlined or not. Hover a name for the whole of it.

FunctionSelfTotal

Hottest lines

LineFunctionShare

How it is made

A GitHub workflow reprofiles the backtest on every change to its code, on the hourly job's latest bars, and this page shows its newest result. By hand, on Linux with valgrind, from the repository root, on a history snapshot or, with no arguments, a synthetic market of the same shape (40 random-walk symbols over 180 days):

scripts/profile.sh data/history-180.bin
scripts/profile.sh

A native run times the phases. A callgrind run of the last eight walked days counts the instructions: every walked day is the same work, so the shares hold for the whole walk. The binary's debug information names the functions the compiler inlined, and each call's site says which inlined function made it. The script writes flamegraph.svg and profile.json; a copy deployed with the site is shown until the workflow has run.