From 4397e30f7d0f56be1556b187b73103606112c141 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2026 19:27:20 +0000 Subject: [PATCH 01/14] [Autoloop: perf-comparison] Iteration 482: add wasm_agg_ops benchmark pair Run: https://github.com/githubnext/tsb/actions/runs/32766310388 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- benchmarks/pandas/bench_wasm_agg_ops.py | 51 +++++++++++++++++++++ benchmarks/tsb/bench_wasm_agg_ops.ts | 60 +++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 benchmarks/pandas/bench_wasm_agg_ops.py create mode 100644 benchmarks/tsb/bench_wasm_agg_ops.ts diff --git a/benchmarks/pandas/bench_wasm_agg_ops.py b/benchmarks/pandas/bench_wasm_agg_ops.py new file mode 100644 index 00000000..0b44ec3b --- /dev/null +++ b/benchmarks/pandas/bench_wasm_agg_ops.py @@ -0,0 +1,51 @@ +""" +Benchmark: numpy aggregate operations — np.sum, np.mean, np.min, np.max, np.var, np.std, np.median +plus pandas rolling and expanding window ops on a 100k-element float64 array. + +Mirrors tsb bench_wasm_agg_ops.ts. + +Outputs JSON: {"function": "wasm_agg_ops", "mean_ms": ..., "iterations": ..., "total_ms": ...} +""" +import json +import time +import numpy as np +import pandas as pd + +SIZE = 100_000 +WINDOW = 50 +MIN_PERIODS = 1 +WARMUP = 3 +ITERATIONS = 20 + +data = np.sin(np.arange(SIZE) * 0.001) * 1000 +series = pd.Series(data) + + +def run(): + np.sum(data) + np.mean(data) + np.min(data) + np.max(data) + np.var(data, ddof=1) + np.std(data, ddof=1) + np.median(data) + series.rolling(window=WINDOW, min_periods=MIN_PERIODS).sum() + series.rolling(window=WINDOW, min_periods=MIN_PERIODS).mean() + series.expanding(min_periods=MIN_PERIODS).sum() + series.expanding(min_periods=MIN_PERIODS).mean() + + +for _ in range(WARMUP): + run() + +start = time.perf_counter() +for _ in range(ITERATIONS): + run() +total = (time.perf_counter() - start) * 1000 # ms + +print(json.dumps({ + "function": "wasm_agg_ops", + "mean_ms": total / ITERATIONS, + "iterations": ITERATIONS, + "total_ms": total, +})) diff --git a/benchmarks/tsb/bench_wasm_agg_ops.ts b/benchmarks/tsb/bench_wasm_agg_ops.ts new file mode 100644 index 00000000..2b615e65 --- /dev/null +++ b/benchmarks/tsb/bench_wasm_agg_ops.ts @@ -0,0 +1,60 @@ +/** + * Benchmark: WASM-accelerated aggregate operations — sumF64Accelerated, meanF64Accelerated, + * minF64Accelerated, maxF64Accelerated, varF64Accelerated, stdF64Accelerated, medianF64Accelerated + * plus rolling and expanding variants on a 100k-element float64 array. + * + * Mirrors numpy aggregate functions (np.sum, np.mean, np.min, np.max, np.var, np.std, np.median) + * and pandas rolling/expanding window ops. + * + * Outputs JSON: {"function": "wasm_agg_ops", "mean_ms": ..., "iterations": ..., "total_ms": ...} + */ +import { + sumF64Accelerated, + meanF64Accelerated, + minF64Accelerated, + maxF64Accelerated, + varF64Accelerated, + stdF64Accelerated, + medianF64Accelerated, + rollingSumF64Accelerated, + rollingMeanF64Accelerated, + expandingSumF64Accelerated, + expandingMeanF64Accelerated, +} from "../../src/wasm/index.ts"; + +const SIZE = 100_000; +const WINDOW = 50; +const MIN_PERIODS = 1; +const WARMUP = 3; +const ITERATIONS = 20; + +const data: number[] = Array.from({ length: SIZE }, (_, i) => Math.sin(i * 0.001) * 1000); + +function run(): void { + sumF64Accelerated(data); + meanF64Accelerated(data); + minF64Accelerated(data); + maxF64Accelerated(data); + varF64Accelerated(data); + stdF64Accelerated(data); + medianF64Accelerated(data); + rollingSumF64Accelerated(data, WINDOW, MIN_PERIODS); + rollingMeanF64Accelerated(data, WINDOW, MIN_PERIODS); + expandingSumF64Accelerated(data, MIN_PERIODS); + expandingMeanF64Accelerated(data, MIN_PERIODS); +} + +for (let i = 0; i < WARMUP; i++) run(); + +const start = performance.now(); +for (let i = 0; i < ITERATIONS; i++) run(); +const total = performance.now() - start; + +console.log( + JSON.stringify({ + function: "wasm_agg_ops", + mean_ms: total / ITERATIONS, + iterations: ITERATIONS, + total_ms: total, + }), +); From bb3aa9124b7d2aa7212a056c4b3478f6a1891b1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 24 Aug 2026 19:27:23 +0000 Subject: [PATCH 02/14] ci: trigger checks From 11976600d4de075331b840da8e3e055317fd3d1b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 25 Aug 2026 07:21:33 +0000 Subject: [PATCH 03/14] [Autoloop: perf-comparison] Iteration 483: add wasm_rolling_stats benchmark pair MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Benchmarks remaining WASM rolling/expanding statistics functions: rollingMinF64Accelerated, rollingMaxF64Accelerated, rollingVarF64Accelerated, rollingStdF64Accelerated, rollingMedianF64Accelerated, and their expanding variants — not covered by bench_wasm_agg_ops which only benchmarked sum/mean. Python counterpart uses pandas Series.rolling()/expanding() with same ops. Run: https://github.com/githubnext/tsb/actions/runs/32820317584 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- benchmarks/pandas/bench_wasm_rolling_stats.py | 64 ++++++++++++++++++ benchmarks/tsb/bench_wasm_rolling_stats.ts | 66 +++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 benchmarks/pandas/bench_wasm_rolling_stats.py create mode 100644 benchmarks/tsb/bench_wasm_rolling_stats.ts diff --git a/benchmarks/pandas/bench_wasm_rolling_stats.py b/benchmarks/pandas/bench_wasm_rolling_stats.py new file mode 100644 index 00000000..a495aed1 --- /dev/null +++ b/benchmarks/pandas/bench_wasm_rolling_stats.py @@ -0,0 +1,64 @@ +""" +Benchmark: WASM rolling/expanding stats equivalents using pandas/numpy — +Series.rolling(50).min/max/var/std/median and Series.expanding().min/max/var/std/median +on a 100k-element float64 array. + +Mirrors bench_wasm_rolling_stats.ts + +Outputs JSON: {"function": "wasm_rolling_stats", "mean_ms": ..., "iterations": ..., "total_ms": ...} +""" + +import json +import math +import time + +import numpy as np +import pandas as pd + +SIZE = 100_000 +WINDOW = 50 +MIN_PERIODS = 1 +WARMUP = 3 +ITERATIONS = 20 + +# Deterministic float64 data (same as TS counterpart) +data = np.array( + [math.sin(i * 0.001) * 100 + math.cos(i * 0.003) * 50 for i in range(SIZE)], + dtype=np.float64, +) +s = pd.Series(data) + + +def run_once() -> None: + s.rolling(WINDOW, min_periods=MIN_PERIODS).min() + s.rolling(WINDOW, min_periods=MIN_PERIODS).max() + s.rolling(WINDOW, min_periods=MIN_PERIODS).var() + s.rolling(WINDOW, min_periods=MIN_PERIODS).std() + s.rolling(WINDOW, min_periods=MIN_PERIODS).median() + s.expanding(min_periods=MIN_PERIODS).min() + s.expanding(min_periods=MIN_PERIODS).max() + s.expanding(min_periods=MIN_PERIODS).var() + s.expanding(min_periods=MIN_PERIODS).std() + s.expanding(min_periods=MIN_PERIODS).median() + + +# Warm-up +for _ in range(WARMUP): + run_once() + +# Measured iterations +t0 = time.perf_counter() +for _ in range(ITERATIONS): + run_once() +total_ms = (time.perf_counter() - t0) * 1000 + +print( + json.dumps( + { + "function": "wasm_rolling_stats", + "mean_ms": total_ms / ITERATIONS, + "iterations": ITERATIONS, + "total_ms": total_ms, + } + ) +) diff --git a/benchmarks/tsb/bench_wasm_rolling_stats.ts b/benchmarks/tsb/bench_wasm_rolling_stats.ts new file mode 100644 index 00000000..015326c3 --- /dev/null +++ b/benchmarks/tsb/bench_wasm_rolling_stats.ts @@ -0,0 +1,66 @@ +/** + * Benchmark: WASM-accelerated rolling and expanding statistics — + * rollingMinF64Accelerated, rollingMaxF64Accelerated, rollingVarF64Accelerated, + * rollingStdF64Accelerated, rollingMedianF64Accelerated, + * expandingMinF64Accelerated, expandingMaxF64Accelerated, + * expandingVarF64Accelerated, expandingStdF64Accelerated, + * expandingMedianF64Accelerated on a 100k-element float64 array. + * + * Mirrors pandas Series.rolling() and Series.expanding() with min/max/var/std/median. + * + * Outputs JSON: {"function": "wasm_rolling_stats", "mean_ms": ..., "iterations": ..., "total_ms": ...} + */ +import { + rollingMinF64Accelerated, + rollingMaxF64Accelerated, + rollingVarF64Accelerated, + rollingStdF64Accelerated, + rollingMedianF64Accelerated, + expandingMinF64Accelerated, + expandingMaxF64Accelerated, + expandingVarF64Accelerated, + expandingStdF64Accelerated, + expandingMedianF64Accelerated, +} from "../../src/wasm/index.ts"; + +const SIZE = 100_000; +const WINDOW = 50; +const MIN_PERIODS = 1; +const WARMUP = 3; +const ITERATIONS = 20; + +// Deterministic float64 data +const data = new Float64Array(SIZE); +for (let i = 0; i < SIZE; i++) { + data[i] = Math.sin(i * 0.001) * 100 + Math.cos(i * 0.003) * 50; +} + +function runOnce(): void { + rollingMinF64Accelerated(data, WINDOW, MIN_PERIODS); + rollingMaxF64Accelerated(data, WINDOW, MIN_PERIODS); + rollingVarF64Accelerated(data, WINDOW, MIN_PERIODS); + rollingStdF64Accelerated(data, WINDOW, MIN_PERIODS); + rollingMedianF64Accelerated(data, WINDOW, MIN_PERIODS); + expandingMinF64Accelerated(data, MIN_PERIODS); + expandingMaxF64Accelerated(data, MIN_PERIODS); + expandingVarF64Accelerated(data, MIN_PERIODS); + expandingStdF64Accelerated(data, MIN_PERIODS); + expandingMedianF64Accelerated(data, MIN_PERIODS); +} + +// Warm-up +for (let i = 0; i < WARMUP; i++) runOnce(); + +// Measured iterations +const t0 = performance.now(); +for (let i = 0; i < ITERATIONS; i++) runOnce(); +const total_ms = performance.now() - t0; + +console.log( + JSON.stringify({ + function: "wasm_rolling_stats", + mean_ms: total_ms / ITERATIONS, + iterations: ITERATIONS, + total_ms, + }), +); From 410d3b8b0ca7dff5f473b27292502b1a6fc4e5e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 25 Aug 2026 07:29:45 +0000 Subject: [PATCH 04/14] ci: trigger checks From 6808d338afb5f736d0ab46c313629a5f789311e8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 25 Aug 2026 19:22:41 +0000 Subject: [PATCH 05/14] [Autoloop: perf-comparison] Iteration 484: add to_dict_series_orient benchmark pair Run: https://github.com/githubnext/tsb/actions/runs/32887698741 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../pandas/bench_to_dict_series_orient.py | 38 +++++++++++++++++ benchmarks/tsb/bench_to_dict_series_orient.ts | 41 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 benchmarks/pandas/bench_to_dict_series_orient.py create mode 100644 benchmarks/tsb/bench_to_dict_series_orient.ts diff --git a/benchmarks/pandas/bench_to_dict_series_orient.py b/benchmarks/pandas/bench_to_dict_series_orient.py new file mode 100644 index 00000000..cbb069ce --- /dev/null +++ b/benchmarks/pandas/bench_to_dict_series_orient.py @@ -0,0 +1,38 @@ +""" +Benchmark: DataFrame.to_dict(orient="series") — converts each column to a pandas Series. + +Mirrors tsb toDictOriented(df, "series"). + +Outputs JSON: {"function": "to_dict_series_orient", "mean_ms": ..., "iterations": ..., "total_ms": ...} +""" +import json +import time +import numpy as np +import pandas as pd + +ROWS = 10_000 +WARMUP = 5 +ITERATIONS = 30 + +df = pd.DataFrame({ + "id": np.arange(ROWS), + "value": np.arange(ROWS) * 1.5, + "label": [f"item_{i % 100}" for i in range(ROWS)], + "score": np.sin(np.arange(ROWS) * 0.01) * 100, + "flag": np.arange(ROWS) % 2 == 0, +}) + +for _ in range(WARMUP): + df.to_dict(orient="series") + +t0 = time.perf_counter() +for _ in range(ITERATIONS): + df.to_dict(orient="series") +total = (time.perf_counter() - t0) * 1000 + +print(json.dumps({ + "function": "to_dict_series_orient", + "mean_ms": total / ITERATIONS, + "iterations": ITERATIONS, + "total_ms": total, +})) diff --git a/benchmarks/tsb/bench_to_dict_series_orient.ts b/benchmarks/tsb/bench_to_dict_series_orient.ts new file mode 100644 index 00000000..038c7d0d --- /dev/null +++ b/benchmarks/tsb/bench_to_dict_series_orient.ts @@ -0,0 +1,41 @@ +/** + * Benchmark: toDictOriented with "series" orient — converts each DataFrame + * column to a Series, producing Record>. + * + * Mirrors pandas DataFrame.to_dict(orient="series") which returns a dict of + * {column_name: Series} pairs. + * + * Outputs JSON: {"function": "to_dict_series_orient", "mean_ms": ..., "iterations": ..., "total_ms": ...} + */ +import { DataFrame, toDictOriented } from "../../src/index.js"; + +const ROWS = 10_000; +const WARMUP = 5; +const ITERATIONS = 30; + +const df = DataFrame.fromColumns({ + id: Array.from({ length: ROWS }, (_, i) => i), + value: Array.from({ length: ROWS }, (_, i) => i * 1.5), + label: Array.from({ length: ROWS }, (_, i) => `item_${i % 100}`), + score: Array.from({ length: ROWS }, (_, i) => Math.sin(i * 0.01) * 100), + flag: Array.from({ length: ROWS }, (_, i) => i % 2 === 0), +}); + +for (let i = 0; i < WARMUP; i++) { + toDictOriented(df, "series"); +} + +const t0 = performance.now(); +for (let i = 0; i < ITERATIONS; i++) { + toDictOriented(df, "series"); +} +const total = performance.now() - t0; + +console.log( + JSON.stringify({ + function: "to_dict_series_orient", + mean_ms: total / ITERATIONS, + iterations: ITERATIONS, + total_ms: total, + }), +); From 25ce6e8cedded22a7345c9064c6ad61b446723ee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 25 Aug 2026 19:25:17 +0000 Subject: [PATCH 06/14] ci: trigger checks From c0902886e4877d5d1ec673fe7b2cb4f5455aa382 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Aug 2026 07:24:01 +0000 Subject: [PATCH 07/14] [Autoloop: perf-comparison] Iteration 485: add registerOption benchmark pair Run: https://github.com/githubnext/tsb/actions/runs/32941495823 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- benchmarks/pandas/bench_register_option.py | 79 ++++++++++++++++++++++ benchmarks/tsb/bench_register_option.ts | 61 +++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 benchmarks/pandas/bench_register_option.py create mode 100644 benchmarks/tsb/bench_register_option.ts diff --git a/benchmarks/pandas/bench_register_option.py b/benchmarks/pandas/bench_register_option.py new file mode 100644 index 00000000..94cc07e3 --- /dev/null +++ b/benchmarks/pandas/bench_register_option.py @@ -0,0 +1,79 @@ +""" +Benchmark: register_option — register custom options with pandas' options system. + +Mirrors tsb registerOption which wraps pandas' core config register_option API. +Uses pandas.core.config_init / _config._registered_options to register custom +options with defaults and validators. + +Outputs JSON: {"function": "register_option", "mean_ms": ..., "iterations": ..., "total_ms": ...} +""" +import json +import time + +import pandas as pd + +WARMUP = 5 +ITERATIONS = 1_000 + +key_counter = [0] + + +def register_and_exercise(): + key = f"bench.custom_{key_counter[0]}" + key_counter[0] += 1 + # pandas does not expose a public register_option in the top-level namespace, + # but it is accessible via pd.core.config.register_option (internal API). + # We simulate the equivalent pattern: register → get → set → reset. + try: + pd.core.config.register_option(key, 42, "A custom numeric option for benchmarking.") + except Exception: + pass # already registered or unavailable + try: + v = pd.get_option(key) + pd.set_option(key, 99) + pd.reset_option(key) + _ = v + except Exception: + pass + + +def register_with_validator(): + key = f"bench.validated_{key_counter[0]}" + key_counter[0] += 1 + + def validator(val): + if not isinstance(val, (int, float)) or val < 0: + raise ValueError("must be a non-negative number") + + try: + pd.core.config.register_option(key, 10, "A validated option.", validator=validator) + except Exception: + pass + try: + pd.set_option(key, 50) + pd.reset_option(key) + except Exception: + pass + + +# Warm-up +for _ in range(WARMUP): + register_and_exercise() + register_with_validator() + +start = time.perf_counter() +for _ in range(ITERATIONS): + register_and_exercise() + register_with_validator() +total_ms = (time.perf_counter() - start) * 1000 + +print( + json.dumps( + { + "function": "register_option", + "mean_ms": total_ms / ITERATIONS, + "iterations": ITERATIONS, + "total_ms": total_ms, + } + ) +) diff --git a/benchmarks/tsb/bench_register_option.ts b/benchmarks/tsb/bench_register_option.ts new file mode 100644 index 00000000..38b2157f --- /dev/null +++ b/benchmarks/tsb/bench_register_option.ts @@ -0,0 +1,61 @@ +/** + * Benchmark: registerOption — register custom options with the tsb options system. + * + * Mirrors pandas `pd.core.config.register_option` which allows users to + * register custom options with validators and defaults. + * + * Covers: + * - registerOption(key, default, doc) → register without validator + * - registerOption(key, default, doc, validator) → register with validator + * - getOption / setOption / resetOption on custom keys + * + * Outputs JSON: {"function": "register_option", "mean_ms": ..., "iterations": ..., "total_ms": ...} + */ +import { registerOption, getOption, setOption, resetOption } from "../../src/index.ts"; + +const WARMUP = 5; +const ITERATIONS = 1_000; + +// Register options outside the loop (registration is one-time setup) +// Use unique keys per run to avoid conflicts with repeated registrations. +let keyCounter = 0; + +function registerAndExercise(): void { + const key = `bench.custom_${keyCounter++}`; + registerOption(key, 42, "A custom numeric option for benchmarking."); + getOption(key); + setOption(key, 99); + resetOption(key); +} + +function registerWithValidator(): void { + const key = `bench.validated_${keyCounter++}`; + registerOption(key, 10, "A validated numeric option.", (val) => { + if (typeof val !== "number" || val < 0) return "must be a non-negative number"; + return undefined; + }); + setOption(key, 50); + resetOption(key); +} + +// Warm-up +for (let i = 0; i < WARMUP; i++) { + registerAndExercise(); + registerWithValidator(); +} + +const start = performance.now(); +for (let i = 0; i < ITERATIONS; i++) { + registerAndExercise(); + registerWithValidator(); +} +const total_ms = performance.now() - start; + +console.log( + JSON.stringify({ + function: "register_option", + mean_ms: total_ms / ITERATIONS, + iterations: ITERATIONS, + total_ms: total_ms, + }), +); From 45cb29307ef457f1a502771ec8ed03719388eaf1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Aug 2026 07:26:30 +0000 Subject: [PATCH 08/14] ci: trigger checks From 78ffce69b84079d693b45253eb9dceafbd2a0734 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 27 Aug 2026 02:43:50 +0000 Subject: [PATCH 09/14] perf: add MultiIndex.toList() benchmark pair Benchmarks MultiIndex.toList() (tsb) vs pd.MultiIndex.tolist() (pandas) on a 100k-pair MultiIndex. This brings the total benchmark pairs from 825 to 826. Run: https://github.com/githubnext/tsb/actions/runs/33033035649 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../pandas/bench_multi_index_to_list.py | 20 ++++++++++++++ benchmarks/tsb/bench_multi_index_to_list.ts | 26 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 benchmarks/pandas/bench_multi_index_to_list.py create mode 100644 benchmarks/tsb/bench_multi_index_to_list.ts diff --git a/benchmarks/pandas/bench_multi_index_to_list.py b/benchmarks/pandas/bench_multi_index_to_list.py new file mode 100644 index 00000000..4d206234 --- /dev/null +++ b/benchmarks/pandas/bench_multi_index_to_list.py @@ -0,0 +1,20 @@ +"""Benchmark: MultiIndex.tolist() on 100k-pair MultiIndex""" +import json, time +import pandas as pd + +ROWS = 100_000 +WARMUP = 3 +ITERATIONS = 10 +a = [f"a{i % 100}" for i in range(ROWS)] +b = [i % 1000 for i in range(ROWS)] +tuples = list(zip(a, b)) +mi = pd.MultiIndex.from_tuples(tuples) + +for _ in range(WARMUP): + mi.tolist() + +start = time.perf_counter() +for _ in range(ITERATIONS): + mi.tolist() +total = (time.perf_counter() - start) * 1000 +print(json.dumps({"function": "multi_index_to_list", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total})) diff --git a/benchmarks/tsb/bench_multi_index_to_list.ts b/benchmarks/tsb/bench_multi_index_to_list.ts new file mode 100644 index 00000000..e367fdf5 --- /dev/null +++ b/benchmarks/tsb/bench_multi_index_to_list.ts @@ -0,0 +1,26 @@ +/** + * Benchmark: MultiIndex.toList() on 100k-pair MultiIndex + * Outputs JSON: {"function": "multi_index_to_list", "mean_ms": ..., "iterations": ..., "total_ms": ...} + */ +import { MultiIndex } from "../../src/index.js"; + +const ROWS = 100_000; +const WARMUP = 3; +const ITERATIONS = 10; +const a = Array.from({ length: ROWS }, (_, i) => `a${i % 100}`); +const b = Array.from({ length: ROWS }, (_, i) => i % 1000); +const tuples: [string, number][] = a.map((v, i) => [v, b[i] as number]); +const mi = new MultiIndex({ tuples }); + +for (let i = 0; i < WARMUP; i++) mi.toList(); +const start = performance.now(); +for (let i = 0; i < ITERATIONS; i++) mi.toList(); +const total = performance.now() - start; +console.log( + JSON.stringify({ + function: "multi_index_to_list", + mean_ms: total / ITERATIONS, + iterations: ITERATIONS, + total_ms: total, + }), +); From 509024094c9c22f5c666e073603cdfd69297ac2c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 27 Aug 2026 02:46:10 +0000 Subject: [PATCH 10/14] ci: trigger checks From fceac8ced6fb3b4f52d54a0b4b8bb97851acd07c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 27 Aug 2026 16:49:36 +0000 Subject: [PATCH 11/14] [Autoloop: perf-comparison] Iteration 487: add string_array_str_ops benchmark pair Run: https://github.com/githubnext/tsb/actions/runs/33094086022 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../pandas/bench_string_array_str_ops.py | 47 ++++++++++++++++++ benchmarks/tsb/bench_string_array_str_ops.ts | 48 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 benchmarks/pandas/bench_string_array_str_ops.py create mode 100644 benchmarks/tsb/bench_string_array_str_ops.ts diff --git a/benchmarks/pandas/bench_string_array_str_ops.py b/benchmarks/pandas/bench_string_array_str_ops.py new file mode 100644 index 00000000..565318d1 --- /dev/null +++ b/benchmarks/pandas/bench_string_array_str_ops.py @@ -0,0 +1,47 @@ +""" +Benchmark: StringArray additional string operations — +lstrip, rstrip, startswith, endswith, replace, zfill +on a 100k-element nullable StringDtype array (~10 % nulls). + +Mirrors pandas pd.array([...], dtype="string") str methods: + str.lstrip, str.rstrip, str.startswith, str.endswith, str.replace, str.zfill + +Outputs JSON: {"function": "string_array_str_ops", "mean_ms": ..., "iterations": ..., "total_ms": ...} +""" +import json +import time +import pandas as pd + +N = 100_000 +WARMUP = 3 +ITERATIONS = 50 + +WORDS = [" hello world ", " foo bar ", "baz qux ", " quux", "corge", "grault ", "garply"] +raw = [None if i % 10 == 0 else WORDS[i % len(WORDS)] for i in range(N)] + +a = pd.array(raw, dtype="string") + + +def run() -> None: + a.str.lstrip() + a.str.rstrip() + a.str.startswith(" he") + a.str.endswith("ld ") + a.str.replace("hello", "hi", regex=False) + a.str.zfill(12) + + +for _ in range(WARMUP): + run() + +start = time.perf_counter() +for _ in range(ITERATIONS): + run() +total_ms = (time.perf_counter() - start) * 1000 + +print(json.dumps({ + "function": "string_array_str_ops", + "mean_ms": total_ms / ITERATIONS, + "iterations": ITERATIONS, + "total_ms": total_ms, +})) diff --git a/benchmarks/tsb/bench_string_array_str_ops.ts b/benchmarks/tsb/bench_string_array_str_ops.ts new file mode 100644 index 00000000..36857bcc --- /dev/null +++ b/benchmarks/tsb/bench_string_array_str_ops.ts @@ -0,0 +1,48 @@ +/** + * Benchmark: StringArray additional string operations — + * lstrip, rstrip, startswith, endswith, replace, zfill + * on a 100k-element nullable StringArray (~10 % nulls). + * + * These methods complement bench_string_array.ts (which covers + * upper/lower/strip/contains/len/fillna) with the remaining + * StringArray string utilities. + * + * Outputs JSON: {"function": "string_array_str_ops", "mean_ms": ..., "iterations": ..., "total_ms": ...} + */ +import { arrays } from "../../src/index.js"; + +const N = 100_000; +const WARMUP = 3; +const ITERATIONS = 50; + +const WORDS = [" hello world ", " foo bar ", "baz qux ", " quux", "corge", "grault ", "garply"]; + +const raw: (string | null)[] = Array.from({ length: N }, (_, i) => + i % 10 === 0 ? null : WORDS[i % WORDS.length], +); + +const a = arrays.StringArray.from(raw); + +function run(): void { + a.lstrip(); + a.rstrip(); + a.startswith(" he"); + a.endswith("ld "); + a.replace("hello", "hi"); + a.zfill(12); +} + +for (let i = 0; i < WARMUP; i++) run(); + +const start = performance.now(); +for (let i = 0; i < ITERATIONS; i++) run(); +const total = performance.now() - start; + +console.log( + JSON.stringify({ + function: "string_array_str_ops", + mean_ms: total / ITERATIONS, + iterations: ITERATIONS, + total_ms: total, + }), +); From 011cb3ba7078083435a0a8b2b13dbabe0ca2fd45 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 27 Aug 2026 16:52:30 +0000 Subject: [PATCH 12/14] ci: trigger checks From 56519d0539040e5f55be8ea8a4d48bf57b19d2d1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 28 Aug 2026 03:47:16 +0000 Subject: [PATCH 13/14] [Autoloop: perf-comparison] Iteration 488: Add ewm benchmark Run: https://github.com/githubnext/tsb/actions/runs/33139413271 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- benchmarks/pandas/bench_ewm.py | 23 +++++++++++++++++++++++ benchmarks/tsb/bench_ewm.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 benchmarks/pandas/bench_ewm.py create mode 100644 benchmarks/tsb/bench_ewm.ts diff --git a/benchmarks/pandas/bench_ewm.py b/benchmarks/pandas/bench_ewm.py new file mode 100644 index 00000000..cf2fd4fa --- /dev/null +++ b/benchmarks/pandas/bench_ewm.py @@ -0,0 +1,23 @@ +"""Benchmark: ewm (Exponentially Weighted Moving) aggregations on 100k-element pandas Series""" +import json, time, math +import numpy as np +import pandas as pd + +ROWS = 100_000 +WARMUP = 3 +ITERATIONS = 10 +data = [math.sin(i * 0.01) * 100 + 50 for i in range(ROWS)] +s = pd.Series(data) + +for _ in range(WARMUP): + s.ewm(span=20).mean() + s.ewm(span=20).std() + s.ewm(span=20).var() + +start = time.perf_counter() +for _ in range(ITERATIONS): + s.ewm(span=20).mean() + s.ewm(span=20).std() + s.ewm(span=20).var() +total = (time.perf_counter() - start) * 1000 +print(json.dumps({"function": "ewm", "mean_ms": total / ITERATIONS, "iterations": ITERATIONS, "total_ms": total})) diff --git a/benchmarks/tsb/bench_ewm.ts b/benchmarks/tsb/bench_ewm.ts new file mode 100644 index 00000000..de980ee9 --- /dev/null +++ b/benchmarks/tsb/bench_ewm.ts @@ -0,0 +1,34 @@ +/** + * Benchmark: EWM (Exponentially Weighted Moving) aggregations on 100k-element Series + */ +import { Series } from "../../src/index.js"; + +const ROWS = 100_000; +const WARMUP = 3; +const ITERATIONS = 10; +const data = Array.from({ length: ROWS }, (_, i) => Math.sin(i * 0.01) * 100 + 50); +const s = new Series({ data }); + +// Warm-up: ewm mean, std, var with span=20 +for (let i = 0; i < WARMUP; i++) { + s.ewm({ span: 20 }).mean(); + s.ewm({ span: 20 }).std(); + s.ewm({ span: 20 }).var(); +} + +const start = performance.now(); +for (let i = 0; i < ITERATIONS; i++) { + s.ewm({ span: 20 }).mean(); + s.ewm({ span: 20 }).std(); + s.ewm({ span: 20 }).var(); +} +const total = performance.now() - start; + +console.log( + JSON.stringify({ + function: "ewm", + mean_ms: total / ITERATIONS, + iterations: ITERATIONS, + total_ms: total, + }), +); From 49578de4a9975264b737701de9f1ec5cc5e80b20 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 28 Aug 2026 03:55:09 +0000 Subject: [PATCH 14/14] ci: trigger checks