Skip to content

Commit 86d054b

Browse files
Iteration 154: Add 5 benchmark pairs (488 total, +5 vs best 483)
Added 5 new pairs: - datetime_index_normalize_filter_shift (DatetimeIndex.normalize/filter/shift) - index_map (Index.map transform function) - multi_index_fromtuples (MultiIndex.fromTuples construction) - timedelta_advanced_ops (Timedelta.parse/toISOString/divBy/negate/mul/compareTo) - dataframe_rolling_var_std_sum_count (DataFrameRolling.var/std/sum/count) Run: https://github.com/githubnext/tsessebe/actions/runs/24565880287 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3e89725 commit 86d054b

10 files changed

Lines changed: 428 additions & 0 deletions
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
Benchmark: pandas DataFrame.rolling().var() / std() / sum() / count() — rolling aggregations.
3+
Outputs JSON: {"function": "dataframe_rolling_var_std_sum_count", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
import numpy as np
9+
10+
SIZE = 50_000
11+
WINDOW = 20
12+
WARMUP = 5
13+
ITERATIONS = 30
14+
15+
df = pd.DataFrame({
16+
"a": np.sin(np.arange(SIZE) * 0.01) * 100,
17+
"b": np.cos(np.arange(SIZE) * 0.01) * 50,
18+
"c": (np.arange(SIZE) % 100) * 1.5,
19+
})
20+
21+
for _ in range(WARMUP):
22+
df.rolling(WINDOW).var()
23+
df.rolling(WINDOW).std()
24+
df.rolling(WINDOW).sum()
25+
df.rolling(WINDOW).count()
26+
27+
times = []
28+
for _ in range(ITERATIONS):
29+
t0 = time.perf_counter()
30+
df.rolling(WINDOW).var()
31+
df.rolling(WINDOW).std()
32+
df.rolling(WINDOW).sum()
33+
df.rolling(WINDOW).count()
34+
times.append((time.perf_counter() - t0) * 1000)
35+
36+
total_ms = sum(times)
37+
mean_ms = total_ms / ITERATIONS
38+
print(json.dumps({
39+
"function": "dataframe_rolling_var_std_sum_count",
40+
"mean_ms": round(mean_ms, 3),
41+
"iterations": ITERATIONS,
42+
"total_ms": round(total_ms, 3),
43+
}))
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Benchmark: pandas DatetimeIndex.normalize() / date filtering / shift — DatetimeIndex transforms.
3+
Outputs JSON: {"function": "datetime_index_normalize_filter_shift", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
SIZE = 5_000
10+
WARMUP = 5
11+
ITERATIONS = 50
12+
13+
idx = pd.date_range(start="2020-01-01 12:30:00", periods=SIZE, freq="h")
14+
cutoff = pd.Timestamp("2021-01-01")
15+
16+
for _ in range(WARMUP):
17+
idx.normalize()
18+
idx[idx < cutoff]
19+
idx.shift(7, freq="D")
20+
21+
times = []
22+
for _ in range(ITERATIONS):
23+
t0 = time.perf_counter()
24+
idx.normalize()
25+
idx[idx < cutoff]
26+
idx.shift(7, freq="D")
27+
times.append((time.perf_counter() - t0) * 1000)
28+
29+
total_ms = sum(times)
30+
mean_ms = total_ms / ITERATIONS
31+
print(json.dumps({
32+
"function": "datetime_index_normalize_filter_shift",
33+
"mean_ms": round(mean_ms, 3),
34+
"iterations": ITERATIONS,
35+
"total_ms": round(total_ms, 3),
36+
}))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Benchmark: pandas Index.map(fn) — transform Index values with a mapping function.
3+
Outputs JSON: {"function": "index_map", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
SIZE = 50_000
10+
WARMUP = 5
11+
ITERATIONS = 50
12+
13+
num_idx = pd.Index(range(SIZE))
14+
str_idx = pd.Index([f"key_{i % 1000}" for i in range(SIZE)])
15+
16+
for _ in range(WARMUP):
17+
num_idx.map(lambda v: v * 2)
18+
str_idx.map(lambda v: v.upper())
19+
20+
times = []
21+
for _ in range(ITERATIONS):
22+
t0 = time.perf_counter()
23+
num_idx.map(lambda v: v * 2)
24+
str_idx.map(lambda v: v.upper())
25+
times.append((time.perf_counter() - t0) * 1000)
26+
27+
total_ms = sum(times)
28+
mean_ms = total_ms / ITERATIONS
29+
print(json.dumps({
30+
"function": "index_map",
31+
"mean_ms": round(mean_ms, 3),
32+
"iterations": ITERATIONS,
33+
"total_ms": round(total_ms, 3),
34+
}))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Benchmark: pandas MultiIndex.from_tuples — construct MultiIndex from array of tuples.
3+
Outputs JSON: {"function": "multi_index_fromtuples", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
SIZE = 5_000
10+
WARMUP = 3
11+
ITERATIONS = 20
12+
13+
tuples2 = [(f"dept_{i % 20}", i % 100) for i in range(SIZE)]
14+
tuples3 = [(f"region_{i % 5}", f"dept_{i % 20}", i % 50) for i in range(SIZE)]
15+
16+
for _ in range(WARMUP):
17+
pd.MultiIndex.from_tuples(tuples2)
18+
pd.MultiIndex.from_tuples(tuples3)
19+
20+
times = []
21+
for _ in range(ITERATIONS):
22+
t0 = time.perf_counter()
23+
pd.MultiIndex.from_tuples(tuples2)
24+
pd.MultiIndex.from_tuples(tuples3)
25+
times.append((time.perf_counter() - t0) * 1000)
26+
27+
total_ms = sum(times)
28+
mean_ms = total_ms / ITERATIONS
29+
print(json.dumps({
30+
"function": "multi_index_fromtuples",
31+
"mean_ms": round(mean_ms, 3),
32+
"iterations": ITERATIONS,
33+
"total_ms": round(total_ms, 3),
34+
}))
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
Benchmark: pandas Timedelta advanced operations — parse, isoformat, division, negation, multiplication, comparison.
3+
Outputs JSON: {"function": "timedelta_advanced_ops", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import pandas as pd
8+
9+
SIZE = 1_000
10+
WARMUP = 5
11+
ITERATIONS = 100
12+
13+
iso_strings = [
14+
"1 days 02:30:00",
15+
"0 days 00:45:00",
16+
"7 days 00:00:00",
17+
"-1 days +22:30:00",
18+
"10 days 05:20:15",
19+
]
20+
21+
td1 = pd.Timedelta(days=2, hours=3)
22+
td2 = pd.Timedelta(hours=5, minutes=30)
23+
deltas = [pd.Timedelta(days=i % 365, hours=i % 24) for i in range(SIZE)]
24+
25+
for _ in range(WARMUP):
26+
for s in iso_strings:
27+
pd.Timedelta(s)
28+
for td in deltas[:50]:
29+
td.isoformat()
30+
td / td1 if td1.total_seconds() != 0 else None
31+
-td
32+
td * 2
33+
td < td2
34+
td == td1
35+
36+
times = []
37+
for _ in range(ITERATIONS):
38+
t0 = time.perf_counter()
39+
for s in iso_strings:
40+
pd.Timedelta(s)
41+
for td in deltas:
42+
td.isoformat()
43+
-td
44+
td * 3
45+
td < td2
46+
td == td1
47+
times.append((time.perf_counter() - t0) * 1000)
48+
49+
total_ms = sum(times)
50+
mean_ms = total_ms / ITERATIONS
51+
print(json.dumps({
52+
"function": "timedelta_advanced_ops",
53+
"mean_ms": round(mean_ms, 3),
54+
"iterations": ITERATIONS,
55+
"total_ms": round(total_ms, 3),
56+
}))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Benchmark: DataFrameRolling.var / std / sum / count — rolling aggregations on a 50k-row DataFrame.
3+
* Outputs JSON: {"function": "dataframe_rolling_var_std_sum_count", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { DataFrame } from "../../src/index.ts";
6+
7+
const SIZE = 50_000;
8+
const WINDOW = 20;
9+
const WARMUP = 5;
10+
const ITERATIONS = 30;
11+
12+
const df = DataFrame.fromColumns({
13+
a: Array.from({ length: SIZE }, (_, i) => Math.sin(i * 0.01) * 100),
14+
b: Array.from({ length: SIZE }, (_, i) => Math.cos(i * 0.01) * 50),
15+
c: Array.from({ length: SIZE }, (_, i) => (i % 100) * 1.5),
16+
});
17+
18+
for (let i = 0; i < WARMUP; i++) {
19+
df.rolling(WINDOW).var();
20+
df.rolling(WINDOW).std();
21+
df.rolling(WINDOW).sum();
22+
df.rolling(WINDOW).count();
23+
}
24+
25+
const times: number[] = [];
26+
for (let i = 0; i < ITERATIONS; i++) {
27+
const t0 = performance.now();
28+
df.rolling(WINDOW).var();
29+
df.rolling(WINDOW).std();
30+
df.rolling(WINDOW).sum();
31+
df.rolling(WINDOW).count();
32+
times.push(performance.now() - t0);
33+
}
34+
35+
const totalMs = times.reduce((a, b) => a + b, 0);
36+
const meanMs = totalMs / ITERATIONS;
37+
console.log(
38+
JSON.stringify({
39+
function: "dataframe_rolling_var_std_sum_count",
40+
mean_ms: Math.round(meanMs * 1000) / 1000,
41+
iterations: ITERATIONS,
42+
total_ms: Math.round(totalMs * 1000) / 1000,
43+
}),
44+
);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Benchmark: DatetimeIndex.normalize() / filter() / shift(n, freq) — DatetimeIndex transforms.
3+
* Outputs JSON: {"function": "datetime_index_normalize_filter_shift", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { date_range } from "../../src/index.ts";
6+
7+
const SIZE = 5_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
// Index with non-midnight times (so normalize actually changes something)
12+
const idx = date_range({ start: "2020-01-01T12:30:00", periods: SIZE, freq: "h" });
13+
const cutoff = new Date("2021-01-01T00:00:00Z");
14+
15+
for (let i = 0; i < WARMUP; i++) {
16+
idx.normalize();
17+
idx.filter((d) => d < cutoff);
18+
idx.shift(7, "D");
19+
}
20+
21+
const times: number[] = [];
22+
for (let i = 0; i < ITERATIONS; i++) {
23+
const t0 = performance.now();
24+
idx.normalize();
25+
idx.filter((d) => d < cutoff);
26+
idx.shift(7, "D");
27+
times.push(performance.now() - t0);
28+
}
29+
30+
const totalMs = times.reduce((a, b) => a + b, 0);
31+
const meanMs = totalMs / ITERATIONS;
32+
console.log(
33+
JSON.stringify({
34+
function: "datetime_index_normalize_filter_shift",
35+
mean_ms: Math.round(meanMs * 1000) / 1000,
36+
iterations: ITERATIONS,
37+
total_ms: Math.round(totalMs * 1000) / 1000,
38+
}),
39+
);

benchmarks/tsb/bench_index_map.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Benchmark: Index.map(fn) — transform Index values to a new Index using a mapping function.
3+
* Outputs JSON: {"function": "index_map", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { Index } from "../../src/index.ts";
6+
7+
const SIZE = 50_000;
8+
const WARMUP = 5;
9+
const ITERATIONS = 50;
10+
11+
const numIdx = new Index(Array.from({ length: SIZE }, (_, i) => i));
12+
const strIdx = new Index(Array.from({ length: SIZE }, (_, i) => `key_${i % 1000}`));
13+
14+
for (let i = 0; i < WARMUP; i++) {
15+
numIdx.map((v) => (v as number) * 2);
16+
strIdx.map((v) => (v as string).toUpperCase());
17+
}
18+
19+
const times: number[] = [];
20+
for (let i = 0; i < ITERATIONS; i++) {
21+
const t0 = performance.now();
22+
numIdx.map((v) => (v as number) * 2);
23+
strIdx.map((v) => (v as string).toUpperCase());
24+
times.push(performance.now() - t0);
25+
}
26+
27+
const totalMs = times.reduce((a, b) => a + b, 0);
28+
const meanMs = totalMs / ITERATIONS;
29+
console.log(
30+
JSON.stringify({
31+
function: "index_map",
32+
mean_ms: Math.round(meanMs * 1000) / 1000,
33+
iterations: ITERATIONS,
34+
total_ms: Math.round(totalMs * 1000) / 1000,
35+
}),
36+
);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Benchmark: MultiIndex.fromTuples — construct a MultiIndex from an array of tuples.
3+
* Outputs JSON: {"function": "multi_index_fromtuples", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
*/
5+
import { MultiIndex } from "../../src/index.ts";
6+
7+
const SIZE = 5_000;
8+
const WARMUP = 3;
9+
const ITERATIONS = 20;
10+
11+
// Build an array of 2-level tuples [string, number]
12+
const tuples: (readonly (string | number)[])[] = Array.from({ length: SIZE }, (_, i) => [
13+
`dept_${i % 20}`,
14+
i % 100,
15+
]);
16+
17+
// Also build 3-level tuples to test deeper nesting
18+
const tuples3: (readonly (string | number)[])[] = Array.from({ length: SIZE }, (_, i) => [
19+
`region_${i % 5}`,
20+
`dept_${i % 20}`,
21+
i % 50,
22+
]);
23+
24+
for (let i = 0; i < WARMUP; i++) {
25+
MultiIndex.fromTuples(tuples);
26+
MultiIndex.fromTuples(tuples3);
27+
}
28+
29+
const times: number[] = [];
30+
for (let i = 0; i < ITERATIONS; i++) {
31+
const t0 = performance.now();
32+
MultiIndex.fromTuples(tuples);
33+
MultiIndex.fromTuples(tuples3);
34+
times.push(performance.now() - t0);
35+
}
36+
37+
const totalMs = times.reduce((a, b) => a + b, 0);
38+
const meanMs = totalMs / ITERATIONS;
39+
console.log(
40+
JSON.stringify({
41+
function: "multi_index_fromtuples",
42+
mean_ms: Math.round(meanMs * 1000) / 1000,
43+
iterations: ITERATIONS,
44+
total_ms: Math.round(totalMs * 1000) / 1000,
45+
}),
46+
);

0 commit comments

Comments
 (0)