From b07ee72945315437ca8497006d250af86962d200 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 10:50:03 +0000 Subject: [PATCH 1/2] Iteration 2: Float64Array parallel values for monomorphic numeric sort Pre-copy finite numeric values into a parallel Float64Array (fvals) during the partition pass. For all-numeric Series the sort comparator now reads fvSlice[a]! - fvSlice[b]! instead of (vals[a] as ...) < (vals[b] as ...), giving the JIT an unboxed, monomorphic Float64Array call site and replacing two branch comparisons with a single FP subtraction per comparison. For string/boolean/mixed Series the existing generic branch comparator is used unchanged. Run: https://github.com/githubnext/tsessebe/actions/runs/24830395075 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/core/series.ts | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/core/series.ts b/src/core/series.ts index bcc5e45a..bbd1bbf8 100644 --- a/src/core/series.ts +++ b/src/core/series.ts @@ -716,25 +716,42 @@ export class Series { const vals = this._values; // Pre-partition NaN/null/undefined from finite values in one pass. - // This removes the NaN check from the comparator's hot path. + // fvals mirrors finBuf: fvals[j] holds the numeric value at finBuf[j] + // so the sort comparator reads a typed Float64Array (not a generic T[]), + // giving the JIT a monomorphic, unboxed call site. const finBuf = new Uint32Array(n); const nanBuf = new Uint32Array(n); + const fvals = new Float64Array(n); let finCount = 0; let nanCount = 0; + let allNumeric = true; for (let i = 0; i < n; i++) { const v = vals[i]; if (v === null || v === undefined || (typeof v === "number" && Number.isNaN(v))) { nanBuf[nanCount++] = i; } else { + if (typeof v === "number") { + fvals[finCount] = v; + } else { + allNumeric = false; + } finBuf[finCount++] = i; } } - // Sort the finite-index slice in-place using an indirect comparator. - // Dispatching to one of two monomorphic comparators avoids a per-call - // branch on `ascending` inside the sort's hot loop. + // Sort the finite-index slice in-place. + // For all-numeric data use the Float64Array subtraction comparator — + // monomorphic, branchless, and JIT-specialisable. + // For mixed/string data fall back to the generic branch comparator. const finSlice = finBuf.subarray(0, finCount); - if (ascending) { + const fvSlice = fvals.subarray(0, finCount); + if (allNumeric) { + if (ascending) { + finSlice.sort((a, b) => fvSlice[a]! - fvSlice[b]!); + } else { + finSlice.sort((a, b) => fvSlice[b]! - fvSlice[a]!); + } + } else if (ascending) { finSlice.sort((a, b) => { const av = vals[a] as number | string | boolean; const bv = vals[b] as number | string | boolean; From 8d1d4a376dbf1d80cacb3c671a4a551bee1a3264 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:31:32 +0000 Subject: [PATCH 2/2] fix: correct numeric fast-path indexing in Series.sortValues Agent-Logs-Url: https://github.com/githubnext/tsessebe/sessions/ecf1056a-d429-45df-964d-d721723e5413 Co-authored-by: mrjf <180956+mrjf@users.noreply.github.com> --- src/core/series.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/core/series.ts b/src/core/series.ts index bbd1bbf8..b0fa064b 100644 --- a/src/core/series.ts +++ b/src/core/series.ts @@ -716,9 +716,8 @@ export class Series { const vals = this._values; // Pre-partition NaN/null/undefined from finite values in one pass. - // fvals mirrors finBuf: fvals[j] holds the numeric value at finBuf[j] - // so the sort comparator reads a typed Float64Array (not a generic T[]), - // giving the JIT a monomorphic, unboxed call site. + // fvals stores numeric values by original row index so the sort comparator + // can read a typed Float64Array (not a generic T[]) at index a/b. const finBuf = new Uint32Array(n); const nanBuf = new Uint32Array(n); const fvals = new Float64Array(n); @@ -731,7 +730,7 @@ export class Series { nanBuf[nanCount++] = i; } else { if (typeof v === "number") { - fvals[finCount] = v; + fvals[i] = v; } else { allNumeric = false; } @@ -744,12 +743,11 @@ export class Series { // monomorphic, branchless, and JIT-specialisable. // For mixed/string data fall back to the generic branch comparator. const finSlice = finBuf.subarray(0, finCount); - const fvSlice = fvals.subarray(0, finCount); if (allNumeric) { if (ascending) { - finSlice.sort((a, b) => fvSlice[a]! - fvSlice[b]!); + finSlice.sort((a, b) => fvals[a]! - fvals[b]!); } else { - finSlice.sort((a, b) => fvSlice[b]! - fvSlice[a]!); + finSlice.sort((a, b) => fvals[b]! - fvals[a]!); } } else if (ascending) { finSlice.sort((a, b) => {