Skip to content

Commit e8ffe01

Browse files
authored
Merge pull request #361 from githubnext/autoloop/perf-comparison
[Autoloop: perf-comparison] Iteration 377: +6 benchmark pairs (720 total)
2 parents 58df80e + 3938fa3 commit e8ffe01

30 files changed

Lines changed: 1143 additions & 2 deletions
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Benchmark: Series.add/sub/mul/div — element-wise arithmetic."""
2+
import json
3+
import time
4+
5+
import pandas as pd
6+
7+
SIZE = 100_000
8+
WARMUP = 5
9+
ITERATIONS = 50
10+
11+
data = [float(i) for i in range(SIZE)]
12+
s = pd.Series(data)
13+
s2 = pd.Series([v * 2 for v in data])
14+
15+
for _ in range(WARMUP):
16+
s.add(10)
17+
s.sub(5)
18+
s.mul(3)
19+
s.div(2)
20+
s.add(s2)
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
s.add(10)
26+
s.sub(5)
27+
s.mul(3)
28+
s.div(2)
29+
s.add(s2)
30+
times.append((time.perf_counter() - t0) * 1000)
31+
32+
total_ms = sum(times)
33+
print(
34+
json.dumps(
35+
{
36+
"function": "add_sub_mul_div",
37+
"mean_ms": round(total_ms / ITERATIONS, 3),
38+
"iterations": ITERATIONS,
39+
"total_ms": round(total_ms, 3),
40+
}
41+
)
42+
)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Benchmark: pd.testing.assert_series_equal / assert_frame_equal / assert_index_equal."""
2+
import json, time
3+
import numpy as np
4+
import pandas as pd
5+
6+
SIZE = 10_000
7+
WARMUP = 5
8+
ITERATIONS = 100
9+
10+
numeric_data = np.arange(SIZE, dtype=float) * 0.1
11+
string_data = [f"item_{i % 200}" for i in range(SIZE)]
12+
bool_data = np.arange(SIZE) % 2 == 0
13+
14+
s1 = pd.Series(numeric_data)
15+
s2 = pd.Series(numeric_data.copy())
16+
s_str1 = pd.Series(string_data)
17+
s_str2 = pd.Series(string_data.copy())
18+
19+
df1 = pd.DataFrame({"a": numeric_data, "b": string_data, "c": bool_data})
20+
df2 = pd.DataFrame({"a": numeric_data.copy(), "b": string_data.copy(), "c": bool_data.copy()})
21+
22+
idx1 = pd.Index(np.arange(SIZE))
23+
idx2 = pd.Index(np.arange(SIZE))
24+
25+
for _ in range(WARMUP):
26+
pd.testing.assert_series_equal(s1, s2)
27+
pd.testing.assert_series_equal(s_str1, s_str2)
28+
pd.testing.assert_frame_equal(df1, df2)
29+
pd.testing.assert_index_equal(idx1, idx2)
30+
31+
start = time.perf_counter()
32+
for _ in range(ITERATIONS):
33+
pd.testing.assert_series_equal(s1, s2)
34+
pd.testing.assert_series_equal(s_str1, s_str2)
35+
pd.testing.assert_frame_equal(df1, df2)
36+
pd.testing.assert_index_equal(idx1, idx2)
37+
total = (time.perf_counter() - start) * 1000
38+
39+
print(json.dumps({
40+
"function": "assert_equal",
41+
"mean_ms": total / ITERATIONS,
42+
"iterations": ITERATIONS,
43+
"total_ms": total,
44+
}))
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Benchmark: bootstrap confidence interval on 1000-element array
2+
Uses percentile method with 500 resamples for a realistic workload.
3+
"""
4+
import json
5+
import time
6+
import numpy as np
7+
8+
N = 1_000
9+
WARMUP = 3
10+
ITERATIONS = 10
11+
12+
rng = np.random.default_rng(42)
13+
data = np.sin(np.arange(N) * 0.01) * 50 + 100
14+
15+
16+
def bootstrap_ci(arr, stat_fn, n_resamples=500, seed=42):
17+
"""Percentile bootstrap CI."""
18+
rng_local = np.random.default_rng(seed)
19+
stats = np.empty(n_resamples)
20+
for i in range(n_resamples):
21+
resample = rng_local.choice(arr, size=len(arr), replace=True)
22+
stats[i] = stat_fn(resample)
23+
return np.percentile(stats, [2.5, 97.5])
24+
25+
26+
def mean_fn(xs):
27+
return np.mean(xs)
28+
29+
30+
for _ in range(WARMUP):
31+
bootstrap_ci(data, mean_fn, n_resamples=500)
32+
33+
start = time.perf_counter()
34+
for _ in range(ITERATIONS):
35+
bootstrap_ci(data, mean_fn, n_resamples=500)
36+
total = (time.perf_counter() - start) * 1000
37+
38+
print(json.dumps({
39+
"function": "bootstrap",
40+
"mean_ms": total / ITERATIONS,
41+
"iterations": ITERATIONS,
42+
"total_ms": total,
43+
}))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Benchmark: pd.concat() with 20 DataFrames — many-frame concatenation on 100k total rows."""
2+
import json
3+
import time
4+
import pandas as pd
5+
6+
N_FRAMES = 20
7+
ROWS_EACH = 5_000
8+
WARMUP = 5
9+
ITERATIONS = 20
10+
11+
frames = [
12+
pd.DataFrame({
13+
"a": [float(f * ROWS_EACH + i) for i in range(ROWS_EACH)],
14+
"b": [(f * ROWS_EACH + i) % 100 for i in range(ROWS_EACH)],
15+
"c": [f"cat_{i % 20}" for i in range(ROWS_EACH)],
16+
})
17+
for f in range(N_FRAMES)
18+
]
19+
20+
for _ in range(WARMUP):
21+
pd.concat(frames)
22+
23+
times = []
24+
for _ in range(ITERATIONS):
25+
t0 = time.perf_counter()
26+
pd.concat(frames)
27+
times.append((time.perf_counter() - t0) * 1000)
28+
29+
total = sum(times)
30+
print(json.dumps({
31+
"function": "concat_many_frames",
32+
"mean_ms": total / ITERATIONS,
33+
"iterations": ITERATIONS,
34+
"total_ms": total,
35+
}))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Benchmark: DataFrame.from_records() — construct a DataFrame from a list of dicts."""
2+
import json
3+
import time
4+
import pandas as pd
5+
6+
ROWS = 20_000
7+
WARMUP = 5
8+
ITERATIONS = 20
9+
10+
records = [
11+
{"id": i, "value": i * 1.5, "category": f"cat_{i % 50}", "score": None if i % 2 == 0 else i * 0.1, "rank": i % 100}
12+
for i in range(ROWS)
13+
]
14+
15+
for _ in range(WARMUP):
16+
pd.DataFrame.from_records(records)
17+
18+
times = []
19+
for _ in range(ITERATIONS):
20+
t0 = time.perf_counter()
21+
pd.DataFrame.from_records(records)
22+
times.append((time.perf_counter() - t0) * 1000)
23+
24+
total = sum(times)
25+
print(json.dumps({
26+
"function": "dataframe_from_records",
27+
"mean_ms": total / ITERATIONS,
28+
"iterations": ITERATIONS,
29+
"total_ms": total,
30+
}))
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Benchmark: DataFrame.items() / iteritems() — iterate over (columnName, Series) pairs."""
2+
import json
3+
import time
4+
import pandas as pd
5+
6+
ROWS = 50_000
7+
WARMUP = 5
8+
ITERATIONS = 50
9+
10+
df = pd.DataFrame({
11+
"a": [float(i) for i in range(ROWS)],
12+
"b": [i % 500 for i in range(ROWS)],
13+
"c": [f"cat_{i % 50}" for i in range(ROWS)],
14+
"d": [i * 0.25 for i in range(ROWS)],
15+
"e": [None if i % 2 == 0 else i * 1.5 for i in range(ROWS)],
16+
"f": [i * 3 for i in range(ROWS)],
17+
})
18+
19+
for _ in range(WARMUP):
20+
n = 0
21+
for _name, _col in df.items():
22+
n += 1
23+
for _name, _col in df.iteritems() if hasattr(df, "iteritems") else df.items():
24+
n += 1
25+
26+
times = []
27+
for _ in range(ITERATIONS):
28+
t0 = time.perf_counter()
29+
n = 0
30+
for _name, _col in df.items():
31+
n += 1
32+
for _name, _col in df.iteritems() if hasattr(df, "iteritems") else df.items():
33+
n += 1
34+
times.append((time.perf_counter() - t0) * 1000)
35+
36+
total = sum(times)
37+
print(json.dumps({
38+
"function": "dataframe_items",
39+
"mean_ms": total / ITERATIONS,
40+
"iterations": ITERATIONS,
41+
"total_ms": total,
42+
}))
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Benchmark: DataFrame.iterrows() — iterate over (label, Series) pairs on a 3k-row DataFrame."""
2+
import json
3+
import time
4+
import pandas as pd
5+
6+
ROWS = 3_000
7+
WARMUP = 5
8+
ITERATIONS = 30
9+
10+
df = pd.DataFrame({
11+
"a": [float(i) for i in range(ROWS)],
12+
"b": [i % 100 for i in range(ROWS)],
13+
"c": [f"cat_{i % 20}" for i in range(ROWS)],
14+
"d": [None if i % 2 == 0 else i * 0.5 for i in range(ROWS)],
15+
"e": [i * 2 for i in range(ROWS)],
16+
})
17+
18+
for _ in range(WARMUP):
19+
n = 0
20+
for _label, _row in df.iterrows():
21+
n += 1
22+
23+
times = []
24+
for _ in range(ITERATIONS):
25+
t0 = time.perf_counter()
26+
n = 0
27+
for _label, _row in df.iterrows():
28+
n += 1
29+
times.append((time.perf_counter() - t0) * 1000)
30+
31+
total = sum(times)
32+
print(json.dumps({
33+
"function": "dataframe_iterrows",
34+
"mean_ms": total / ITERATIONS,
35+
"iterations": ITERATIONS,
36+
"total_ms": total,
37+
}))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Benchmark: DataFrame.groupby().sum() with 1000 groups on a 100k-row DataFrame."""
2+
import json
3+
import time
4+
import pandas as pd
5+
6+
ROWS = 100_000
7+
N_GROUPS = 1_000
8+
WARMUP = 3
9+
ITERATIONS = 10
10+
11+
df = pd.DataFrame({
12+
"key": [f"g{i % N_GROUPS}" for i in range(ROWS)],
13+
"val1": [i * 0.5 for i in range(ROWS)],
14+
"val2": [i % 200 for i in range(ROWS)],
15+
})
16+
17+
for _ in range(WARMUP):
18+
df.groupby("key").sum()
19+
20+
times = []
21+
for _ in range(ITERATIONS):
22+
t0 = time.perf_counter()
23+
df.groupby("key").sum()
24+
times.append((time.perf_counter() - t0) * 1000)
25+
26+
total = sum(times)
27+
print(json.dumps({
28+
"function": "groupby_sum_many_groups",
29+
"mean_ms": total / ITERATIONS,
30+
"iterations": ITERATIONS,
31+
"total_ms": total,
32+
}))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Benchmark: pd.Grouper construction and isinstance checks — 50k iterations."""
2+
import json
3+
import time
4+
5+
import pandas as pd
6+
7+
WARMUP = 5
8+
ITERATIONS = 50_000
9+
10+
11+
def run_groupers() -> None:
12+
g1 = pd.Grouper(key="col_a")
13+
g2 = pd.Grouper(key="date", sort=True)
14+
g3 = pd.Grouper(key="category", dropna=False)
15+
16+
isinstance(g1, pd.Grouper)
17+
isinstance(g2, pd.Grouper)
18+
isinstance(g3, pd.Grouper)
19+
isinstance("not_a_grouper", pd.Grouper)
20+
isinstance(42, pd.Grouper)
21+
22+
str(g1)
23+
str(g2)
24+
str(g3)
25+
26+
27+
for _ in range(WARMUP):
28+
run_groupers()
29+
30+
start = time.perf_counter()
31+
for _ in range(ITERATIONS):
32+
run_groupers()
33+
total = (time.perf_counter() - start) * 1000
34+
35+
print(
36+
json.dumps(
37+
{
38+
"function": "grouper_class",
39+
"mean_ms": total / ITERATIONS,
40+
"iterations": ITERATIONS,
41+
"total_ms": total,
42+
}
43+
)
44+
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Benchmark: pd.merge_ordered with left_by grouping — two 3k-row DataFrames, 10 groups."""
2+
import json
3+
import time
4+
5+
import pandas as pd
6+
7+
N = 3_000
8+
GROUPS = 10
9+
PER_GROUP = N // GROUPS
10+
WARMUP = 2
11+
ITERATIONS = 8
12+
13+
grp_left = [f"g{g}" for g in range(GROUPS) for _ in range(PER_GROUP)]
14+
t_left = [j * 2 for _ in range(GROUPS) for j in range(PER_GROUP)]
15+
v1 = [g * PER_GROUP + j for g in range(GROUPS) for j in range(PER_GROUP)]
16+
17+
grp_right = [f"g{g}" for g in range(GROUPS) for _ in range(PER_GROUP)]
18+
t_right = [j * 3 for _ in range(GROUPS) for j in range(PER_GROUP)]
19+
v2 = [g * PER_GROUP + j for g in range(GROUPS) for j in range(PER_GROUP)]
20+
21+
df1 = pd.DataFrame({"grp": grp_left, "t": t_left, "val1": v1})
22+
df2 = pd.DataFrame({"grp": grp_right, "t": t_right, "val2": v2})
23+
24+
for _ in range(WARMUP):
25+
pd.merge_ordered(df1, df2, on="t", left_by="grp", right_by="grp")
26+
27+
start = time.perf_counter()
28+
for _ in range(ITERATIONS):
29+
pd.merge_ordered(df1, df2, on="t", left_by="grp", right_by="grp")
30+
total = (time.perf_counter() - start) * 1000
31+
32+
print(
33+
json.dumps(
34+
{
35+
"function": "merge_ordered_by",
36+
"mean_ms": total / ITERATIONS,
37+
"iterations": ITERATIONS,
38+
"total_ms": total,
39+
}
40+
)
41+
)

0 commit comments

Comments
 (0)