diff --git a/.jules/bolt.md b/.jules/bolt.md index 2fd177f..84f6346 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -26,3 +26,8 @@ ## 2024-05-18 - [SQLite Statement Caching] **Learning:** better-sqlite3 `db.prepare()` is expensive enough that calling it in a hot loop (like `cached()`) causes significant performance overhead (~4x slower). Statement caching per-database instance is required for hot path queries. **Action:** When using better-sqlite3 in frequently called functions (e.g. `cached()` wrapper or event loop hooks), lazily initialize and reuse prepared statements bound to the current database instance instead of preparing the statement on every call. + +## 2026-09-28 - Avoid chained map and slice in hot paths +**Learning:** In `src/tools/technical.ts`, the `indicatorsTool` used chained array methods like `.map()` and `.slice().map()` to extract closes and construct the resulting bars array. This created intermediate array allocations causing significant garbage collection overhead during hot-path technical indicator computation. +**Action:** Replaced the chained array methods with a single, highly optimized indexed `for` loop to reduce memory allocations and avoid GC pressure. + diff --git a/src/tools/technical.ts b/src/tools/technical.ts index 96223bd..e389911 100644 --- a/src/tools/technical.ts +++ b/src/tools/technical.ts @@ -75,12 +75,27 @@ export const indicatorsTool = tool( bbStdDev, }) => { const series = await loadCloses(symbol, kind, resolution as Resolution, bars); - const closes = series.map((b) => b.close); + + const len = series.length; + // Avoid chained .map() and .slice().map() to reduce memory allocation + const closes = new Array(len); + for (let i = 0; i < len; i++) { + closes[i] = series[i]!.close; + } + + const sliceLen = Math.min(len, bars); + const startIdx = len - sliceLen; + const outBars = new Array(sliceLen); + for (let i = 0; i < sliceLen; i++) { + const b = series[startIdx + i]!; + outBars[i] = { time: b.time, close: b.close }; + } + const out: Record = { symbol, kind, resolution, - bars: series.slice(-bars).map((b) => ({ time: b.time, close: b.close })), + bars: outBars, latest_close: last(closes), };