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 1/8] [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 2/8] 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 3/8] [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 4/8] 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 5/8] [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 6/8] 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 7/8] [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 8/8] ci: trigger checks