Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4397e30
[Autoloop: perf-comparison] Iteration 482: add wasm_agg_ops benchmark…
github-actions[bot] Aug 24, 2026
bb3aa91
ci: trigger checks
github-actions[bot] Aug 24, 2026
1197660
[Autoloop: perf-comparison] Iteration 483: add wasm_rolling_stats ben…
github-actions[bot] Aug 25, 2026
410d3b8
ci: trigger checks
github-actions[bot] Aug 25, 2026
6808d33
[Autoloop: perf-comparison] Iteration 484: add to_dict_series_orient …
github-actions[bot] Aug 25, 2026
25ce6e8
ci: trigger checks
github-actions[bot] Aug 25, 2026
c090288
[Autoloop: perf-comparison] Iteration 485: add registerOption benchma…
github-actions[bot] Aug 26, 2026
45cb293
ci: trigger checks
github-actions[bot] Aug 26, 2026
78ffce6
perf: add MultiIndex.toList() benchmark pair
github-actions[bot] Aug 27, 2026
5090240
ci: trigger checks
github-actions[bot] Aug 27, 2026
fceac8c
[Autoloop: perf-comparison] Iteration 487: add string_array_str_ops b…
github-actions[bot] Aug 27, 2026
011cb3b
ci: trigger checks
github-actions[bot] Aug 27, 2026
56519d0
[Autoloop: perf-comparison] Iteration 488: Add ewm benchmark
github-actions[bot] Aug 28, 2026
49578de
ci: trigger checks
github-actions[bot] Aug 28, 2026
85dc945
[Autoloop: perf-comparison] Iteration 489: add string_array_cat bench…
github-actions[bot] Aug 28, 2026
c1f5164
ci: trigger checks
github-actions[bot] Aug 28, 2026
fd0704a
[Autoloop: perf-comparison] Iteration 490: add series_rename_ops benc…
github-actions[bot] Aug 29, 2026
3d3bdcb
ci: trigger checks
github-actions[bot] Aug 29, 2026
5d7effd
[Autoloop: perf-comparison] Iteration 491: add wasm_rolling_sum_mean …
github-actions[bot] Aug 29, 2026
ccb4fca
ci: trigger checks
github-actions[bot] Aug 29, 2026
dce1128
[Autoloop: perf-comparison] Iteration 492: add datetime_index_min_max…
github-actions[bot] Aug 30, 2026
59b9e38
ci: trigger checks
github-actions[bot] Aug 30, 2026
bbd59e8
[Autoloop: perf-comparison] Iteration 493: add sparse_array_advanced …
github-actions[bot] Aug 30, 2026
46d1ff2
ci: trigger checks
github-actions[bot] Aug 30, 2026
9855436
[Autoloop: perf-comparison] Iteration 494: add wasm_expanding_stats b…
github-actions[bot] Aug 30, 2026
c070e2f
ci: trigger checks
github-actions[bot] Aug 30, 2026
85b793c
[Autoloop: perf-comparison] Iteration 495: Add stack/unstack benchmark
github-actions[bot] Aug 31, 2026
180ef68
ci: trigger checks
github-actions[bot] Aug 31, 2026
bd27092
[Autoloop: perf-comparison] Iteration 496: bench seriesDigitize and c…
github-actions[bot] Aug 31, 2026
bae8fc7
ci: trigger checks
github-actions[bot] Aug 31, 2026
ef191e6
[Autoloop: perf-comparison] Iteration 497: Add CategoricalAccessor mu…
github-actions[bot] Sep 1, 2026
d6a2ece
ci: trigger checks
github-actions[bot] Sep 1, 2026
5410407
[Autoloop: perf-comparison] Iteration 498: Add wasm_agg_scalar benchmark
github-actions[bot] Sep 6, 2026
2428415
ci: trigger checks
mrjf Sep 6, 2026
fbd8aa0
[Autoloop: perf-comparison] Iteration 498: Add combine_series_datafra…
github-actions[bot] Sep 9, 2026
3e986d4
ci: trigger checks
mrjf Sep 9, 2026
d653df9
[Autoloop: perf-comparison] Iteration 499: Add errors_extended benchm…
github-actions[bot] Sep 10, 2026
5287d44
ci: trigger checks
mrjf Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions benchmarks/pandas/bench_cat_accessor_mutation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import json
import time
import pandas as pd

N = 50_000
CATS = ["alpha", "beta", "gamma", "delta", "epsilon"]
data = [CATS[i % len(CATS)] for i in range(N)]
series = pd.Series(pd.Categorical(data, categories=CATS))

WARMUP = 5
ITERATIONS = 50


def run():
# remove_categories — remove an absent category (safe no-op)
series.cat.remove_categories([])

# rename_categories — rename via dict
series.cat.rename_categories(
{"alpha": "a", "beta": "b", "gamma": "c", "delta": "d", "epsilon": "e"}
)

# set_categories — replace with a superset
series.cat.set_categories(
["alpha", "beta", "gamma", "delta", "epsilon", "zeta"], ordered=False
)

# reorder_categories — same set, different order
series.cat.reorder_categories(["epsilon", "delta", "gamma", "beta", "alpha"])

# as_ordered / as_unordered — flip ordered flag
series.cat.as_ordered()
series.cat.as_unordered()


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": "cat_accessor_mutation",
"mean_ms": total_ms / ITERATIONS,
"iterations": ITERATIONS,
"total_ms": total_ms,
}
)
)
48 changes: 48 additions & 0 deletions benchmarks/pandas/bench_combine_series_dataframe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Benchmark: DataFrame.combine / Series.combine — element-wise combine of two
Series (and column-by-column combine of two DataFrames) with a binary function.
"""
import json
import time

import pandas as pd
import numpy as np

N = 50_000
WARMUP = 2
ITERATIONS = 5

idx = np.arange(N)
a = pd.Series(np.arange(N, dtype=np.float64), index=idx)
b = pd.Series(np.arange(N, dtype=np.float64) * 2.0, index=idx)

df_a = pd.DataFrame({"x": a.values, "y": b.values})
df_b = pd.DataFrame({"x": b.values, "z": a.values})


def add(p, q):
p = 0 if pd.isna(p) else p
q = 0 if pd.isna(q) else q
return p + q


for _ in range(WARMUP):
a.combine(b, add, fill_value=0)
df_a.combine(df_b, lambda s1, s2: s1.combine(s2, add, fill_value=0), fill_value=0)

start = time.perf_counter()
for _ in range(ITERATIONS):
a.combine(b, add, fill_value=0)
df_a.combine(df_b, lambda s1, s2: s1.combine(s2, add, fill_value=0), fill_value=0)
total = (time.perf_counter() - start) * 1000

print(
json.dumps(
{
"function": "combine_series_dataframe",
"mean_ms": total / ITERATIONS,
"iterations": ITERATIONS,
"total_ms": total,
}
)
)
45 changes: 45 additions & 0 deletions benchmarks/pandas/bench_datetime_index_min_max.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
Benchmark: pandas DatetimeIndex — min(), max(), index access, to_pydatetime(), asi8
on a 10,000-element DatetimeIndex.

Mirrors tsb bench_datetime_index_min_max.ts.

Outputs JSON: {"function": "datetime_index_min_max", "mean_ms": ..., "iterations": ..., "total_ms": ...}
"""
import json
import time
import pandas as pd

SIZE = 10_000
WARMUP = 5
ITERATIONS = 50

idx = pd.date_range(start="2000-01-01", periods=SIZE, freq="h")
mid = SIZE // 2

# Warm-up
for _ in range(WARMUP):
idx.min()
idx.max()
_ = idx[mid]
idx.to_pydatetime()
idx.asi8

start = time.perf_counter()
for _ in range(ITERATIONS):
idx.min()
idx.max()
_ = idx[mid]
idx.to_pydatetime()
idx.asi8
total_s = time.perf_counter() - start

total_ms = total_s * 1000
mean_ms = total_ms / ITERATIONS

print(json.dumps({
"function": "datetime_index_min_max",
"mean_ms": mean_ms,
"iterations": ITERATIONS,
"total_ms": total_ms,
}))
111 changes: 111 additions & 0 deletions benchmarks/pandas/bench_errors_extended.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
Benchmark: pd.errors namespace (extended) — remaining pandas-compatible
error/warning classes not covered by bench_errors.py.

Mirrors tsb's `errors` namespace additions: AbstractMethodError,
AccessorRegistrationWarning, AttributeConflictWarning, CSSWarning,
ChainedAssignmentError, DatabaseError, DtypeWarning, DuplicateLabelError,
InvalidColumnName, InvalidComparison, InvalidUseOfBooleanIndex,
InvalidVersion, LossySetitemError, NullFrequencyError, NumbaUtilError,
OutOfBoundsDatetime, OutOfBoundsTimedelta, ParserWarning,
PossibleDataLossError, PossiblePrecisionLoss, SpecificationError,
UnsupportedFunctionCall, ValueLabelTypeMismatch.

A handful of these names (InvalidColumnName, InvalidUseOfBooleanIndex,
LossySetitemError, ValueLabelTypeMismatch) are tsb-specific extensions with
no direct `pandas.errors` counterpart across all pandas versions, so a local
fallback class is used for those to keep the benchmark portable.

Outputs JSON: {"function": "errors_extended", "mean_ms": ..., "iterations": ..., "total_ms": ...}
"""

import json
import time

import pandas.errors as pd_errors


class _FallbackError(ValueError):
"""Local stand-in for tsb-only error classes with no pandas.errors match."""


InvalidColumnName = getattr(pd_errors, "InvalidColumnName", _FallbackError)
InvalidUseOfBooleanIndex = getattr(pd_errors, "InvalidUseOfBooleanIndex", _FallbackError)
LossySetitemError = getattr(pd_errors, "LossySetitemError", _FallbackError)
ValueLabelTypeMismatch = getattr(pd_errors, "ValueLabelTypeMismatch", _FallbackError)

WARMUP = 5
ITERATIONS = 200


def _run():
e1 = pd_errors.AbstractMethodError(object())
e2 = pd_errors.AccessorRegistrationWarning("shadowed accessor")
e3 = pd_errors.AttributeConflictWarning("conflicting attribute")
e4 = pd_errors.CSSWarning("bad css rule")
e5 = pd_errors.ChainedAssignmentError()
e6 = pd_errors.DatabaseError("connection failed")
e7 = pd_errors.DtypeWarning("mismatched dtypes")
e8 = pd_errors.DuplicateLabelError("Index has duplicates")
e9 = InvalidColumnName("bad column")
e10 = pd_errors.InvalidComparison("incompatible types")
e11 = InvalidUseOfBooleanIndex("bad boolean index")
e12 = pd_errors.InvalidVersion("not.a.version")
e13 = LossySetitemError("would lose precision")
e14 = pd_errors.NullFrequencyError("null frequency")
e15 = pd_errors.NumbaUtilError("numba failure")
e16 = pd_errors.OutOfBoundsDatetime("out of bounds timestamp")
e17 = pd_errors.OutOfBoundsTimedelta("out of bounds timedelta")
e18 = pd_errors.ParserWarning("falling back to python engine")
e19 = pd_errors.PossibleDataLossError("mode='w' will overwrite")
e20 = pd_errors.PossiblePrecisionLoss("float64 -> float32")
e21 = pd_errors.SpecificationError("nested renamer is not supported")
e22 = pd_errors.UnsupportedFunctionCall("numpy operation not supported")
e23 = ValueLabelTypeMismatch("mismatched value/label types")

_a = isinstance(e1, Exception)
_b = isinstance(e2, pd_errors.AccessorRegistrationWarning)
_c = type(e3).__name__ == "AttributeConflictWarning"
_d = type(e4).__name__ == "CSSWarning"
_e = "copy" in str(e5) or True
_f = isinstance(e6, pd_errors.DatabaseError)
_g = type(e7).__name__ == "DtypeWarning"
_h = isinstance(e8, ValueError)
_i = type(e9).__name__ in ("InvalidColumnName", "_FallbackError")
_j = isinstance(e10, TypeError)
_k = isinstance(e11, (IndexError, ValueError))
_l = isinstance(e12, ValueError)
_m = type(e13).__name__ in ("LossySetitemError", "_FallbackError")
_n = isinstance(e14, ValueError)
_o = type(e15).__name__ == "NumbaUtilError"
_p = isinstance(e16, ValueError)
_q = isinstance(e17, ValueError)
_r = type(e18).__name__ == "ParserWarning"
_s = type(e19).__name__ == "PossibleDataLossError"
_t = type(e20).__name__ == "PossiblePrecisionLoss"
_u = isinstance(e21, ValueError)
_v = isinstance(e22, ValueError)
_w = type(e23).__name__ in ("ValueLabelTypeMismatch", "_FallbackError")
return [
_a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w,
]


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": "errors_extended",
"mean_ms": total_ms / ITERATIONS,
"iterations": ITERATIONS,
"total_ms": total_ms,
}
)
)
23 changes: 23 additions & 0 deletions benchmarks/pandas/bench_ewm.py
Original file line number Diff line number Diff line change
@@ -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}))
20 changes: 20 additions & 0 deletions benchmarks/pandas/bench_multi_index_to_list.py
Original file line number Diff line number Diff line change
@@ -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}))
79 changes: 79 additions & 0 deletions benchmarks/pandas/bench_register_option.py
Original file line number Diff line number Diff line change
@@ -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,
}
)
)
Loading
Loading