plugins/ruflo-neural-trader/docs/perf-notes.md
Bench-driven hot-path analysis derived from the three baseline benches
landed in #48:
benchmarks/signal-generation.bench.mjsbenchmarks/backtest-throughput.bench.mjsbenchmarks/memory-recall.bench.mjsThe portfolio CG bench (portfolio-cg.bench.mjs) is owned by the
ADR-126 Phase 3 / #2080 work and is referenced read-only here.
plugins/ruflo-neural-trader/src/sublinear-adapter.mjs:148-184
(neumannSeries exported kernel)new Float64Array(n) allocation with
a two-buffer ping-pong (cur / next, swapped by reference each iter).
At n=256 a typical 5-iter solve previously allocated 5×2 KB of garbage
per call — eliminated.neural-trader change needed.
Smoke node scripts/smoke-neural-trader-portfolio-cg.mjs still passes.||cg − neumann||_∞ = 2.12e-8 at n=256 — identical
to the pre-change baseline; the change is purely an allocation-pattern
rewrite, not an algorithmic change.mcp__claude-flow__memory_search
via AgentDB + HNSW. The bench memory-recall.bench.mjs models the
linear-scan baseline (cosine over Float32Array) to track the
approximate-vs-exact recall gap an ANN index would create.memory_search
already uses HNSW per ADR-006, which gives 150x–12,500× speedups on
this exact workload. The plugin doesn't need a local change — what we
DO need is a regression gate: when the skill's recall@10 drops below
0.8, the HNSW build params (M, efConstruction) drifted and need a
rebuild. The bench captures that gate. Scope: upstream HNSW config,
not the plugin.benchmarks/signal-generation.bench.mjs — for AAPL the
measured avg is 22 µs but p99 is 191 µs.bars.map(b => b.close) + two
bars.slice calls per scan in the bench. The production --signal scan
path is upstream (npx neural-trader) and we can't fix it in the
plugin layer, but the bench is illustrative.closes and slice via indices instead of materializing sub-arrays.
Saves ~3 allocations per scan call.neural-trader binary if the same pattern exists
in the Rust/NAPI code path — confirmed only by profiling the
--signal scan cloud roundtrip, which the plugin can't reach from a
pure-JS smoke.canonicalBytes (signed-artifact.mjs:75 / signed-attribution.mjs:193) —
the explicit ADR-126 Phase 4 contract is JSON.stringify with NO key
sort, deterministic-by-construction. Adding a sort or memoization
would break the smoke's CWE-347 parity with the plugin-registry
signer. Leave as-is.isSymmetric (sublinear-adapter.mjs:204-211) — already short-circuits
on first violation. The full upper-triangle scan on symmetric inputs
is the contract guard the smoke depends on; weakening it weakens the
SPD precondition.The three new benches act as a perf budget:
| Bench | Budget | Source |
|---|---|---|
| signal worst-symbol avg | <1000 µs | acceptance line |
| signal full-scan sum-of-avgs | <10 ms | acceptance line |
| backtest avg runtime | <10 ms | acceptance line |
| backtest throughput | >25,000 bars/sec | acceptance line |
| memory-recall p95 at N=5000 | <50 ms | acceptance line |
| memory-recall recall@10 | >=0.8 (90% subset) | acceptance line |
A bench whose acceptance line trips on CI is a perf regression.