From d31aa7dfc9bd4d8e0aceb925d236ae940b55690d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 04:03:45 +0000 Subject: [PATCH 1/2] =?UTF-8?q?Iteration=201:=20Island=201=20=E2=80=94=20i?= =?UTF-8?q?ndirect=20typed-array=20sort=20with=20NaN=20pre-partition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run: https://github.com/githubnext/tsessebe/actions/runs/24815695413 - Replace boxed {v,i} pair allocation with Uint32Array index sort - Pre-partition NaN/null/undefined in one pass (removes NaN check from comparator hot path) - Dispatch to one of two monomorphic comparators (ascending vs descending) - Single gather loop instead of two .map() calls - Reduces GC pressure from N object allocations to zero Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/core/series.ts | 70 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/src/core/series.ts b/src/core/series.ts index 3dd5ee16..7a97a2d8 100644 --- a/src/core/series.ts +++ b/src/core/series.ts @@ -712,11 +712,73 @@ export class Series { /** Return a new Series sorted by values. */ sortValues(ascending = true, naPosition: "first" | "last" = "last"): Series { - const pairs = this._values.map((v, i) => ({ v, i })); - pairs.sort((a, b) => compareScalars(a.v, b.v, ascending, naPosition)); + const n = this._values.length; + 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. + const finBuf = new Uint32Array(n); + const nanBuf = new Uint32Array(n); + let finCount = 0; + let nanCount = 0; + 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 { + 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. + const finSlice = finBuf.subarray(0, finCount); + if (ascending) { + finSlice.sort((a, b) => { + const av = vals[a] as number | string | boolean; + const bv = vals[b] as number | string | boolean; + return av < bv ? -1 : av > bv ? 1 : 0; + }); + } else { + finSlice.sort((a, b) => { + const av = vals[a] as number | string | boolean; + const bv = vals[b] as number | string | boolean; + return av > bv ? -1 : av < bv ? 1 : 0; + }); + } + + // Build the output permutation and gather values in a single pass. + const perm = new Array(n); + const outData = new Array(n); + let pos = 0; + if (naPosition === "first") { + for (let i = 0; i < nanCount; i++) { + const idx = nanBuf[i]; + perm[pos] = idx; + outData[pos++] = vals[idx] as T; + } + for (let i = 0; i < finCount; i++) { + const idx = finSlice[i]; + perm[pos] = idx; + outData[pos++] = vals[idx] as T; + } + } else { + for (let i = 0; i < finCount; i++) { + const idx = finSlice[i]; + perm[pos] = idx; + outData[pos++] = vals[idx] as T; + } + for (let i = 0; i < nanCount; i++) { + const idx = nanBuf[i]; + perm[pos] = idx; + outData[pos++] = vals[idx] as T; + } + } + return new Series({ - data: pairs.map(({ v }) => v), - index: this.index.take(pairs.map(({ i }) => i)), + data: outData, + index: this.index.take(perm), dtype: this.dtype, name: this.name, }); From b230a018a53116ae417a51f9fdf5b29a6a62d8b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 05:14:26 +0000 Subject: [PATCH 2/2] fix: add non-null assertions for Uint32Array index access in sortValues noUncheckedIndexedAccess causes Uint32Array element access to return number | undefined. Since loop bounds guarantee valid indices, use ! to assert non-null and satisfy the type checker. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/core/series.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/series.ts b/src/core/series.ts index 7a97a2d8..08932dab 100644 --- a/src/core/series.ts +++ b/src/core/series.ts @@ -754,23 +754,23 @@ export class Series { let pos = 0; if (naPosition === "first") { for (let i = 0; i < nanCount; i++) { - const idx = nanBuf[i]; + const idx = nanBuf[i]!; perm[pos] = idx; outData[pos++] = vals[idx] as T; } for (let i = 0; i < finCount; i++) { - const idx = finSlice[i]; + const idx = finSlice[i]!; perm[pos] = idx; outData[pos++] = vals[idx] as T; } } else { for (let i = 0; i < finCount; i++) { - const idx = finSlice[i]; + const idx = finSlice[i]!; perm[pos] = idx; outData[pos++] = vals[idx] as T; } for (let i = 0; i < nanCount; i++) { - const idx = nanBuf[i]; + const idx = nanBuf[i]!; perm[pos] = idx; outData[pos++] = vals[idx] as T; }