Skip to content

Commit da7e31e

Browse files
authored
Merge pull request #148 from githubnext/autoloop/perf-comparison-eef4c65f
[Autoloop] [Autoloop: perf-comparison]
2 parents 7d019b0 + 1df361d commit da7e31e

52 files changed

Lines changed: 1870 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Benchmark: pandas Series.apply() with (value) lambda — 100k-element Series.
3+
Mirrors tsb's applySeries (stats/apply.ts) behavior.
4+
Outputs JSON: {"function": "applySeries_fn", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
"""
6+
import json
7+
import time
8+
import pandas as pd
9+
10+
SIZE = 100_000
11+
WARMUP = 5
12+
ITERATIONS = 30
13+
14+
s = pd.Series([i * 0.5 for i in range(SIZE)])
15+
16+
fn = lambda v: v * 2 + 1 # noqa: E731
17+
18+
for _ in range(WARMUP):
19+
s.apply(fn)
20+
21+
times = []
22+
for _ in range(ITERATIONS):
23+
t0 = time.perf_counter()
24+
s.apply(fn)
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": "applySeries_fn",
31+
"mean_ms": mean_ms,
32+
"iterations": ITERATIONS,
33+
"total_ms": total_ms,
34+
}))
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
Benchmark: pandas CategoricalIndex modification — rename_categories, reorder_categories,
3+
remove_categories, set_categories, remove_unused_categories on a 10k-element index.
4+
Outputs JSON: {"function": "categorical_index_modify", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
"""
6+
import json
7+
import time
8+
import pandas as pd
9+
10+
SIZE = 10_000
11+
WARMUP = 5
12+
ITERATIONS = 50
13+
14+
CATS = ["alpha", "beta", "gamma", "delta", "epsilon"]
15+
labels = [CATS[i % len(CATS)] for i in range(SIZE)]
16+
ci = pd.CategoricalIndex(labels)
17+
18+
for _ in range(WARMUP):
19+
ci.rename_categories(["A", "B", "C", "D", "E"])
20+
ci.reorder_categories(["epsilon", "delta", "gamma", "beta", "alpha"])
21+
ci.remove_categories(["epsilon"])
22+
ci.set_categories(["alpha", "beta", "gamma"])
23+
ci.remove_unused_categories()
24+
ci.as_ordered()
25+
ci.as_unordered()
26+
27+
times = []
28+
for _ in range(ITERATIONS):
29+
t0 = time.perf_counter()
30+
ci.rename_categories(["A", "B", "C", "D", "E"])
31+
ci.reorder_categories(["epsilon", "delta", "gamma", "beta", "alpha"])
32+
ci.remove_categories(["epsilon"])
33+
ci.set_categories(["alpha", "beta", "gamma"])
34+
ci.remove_unused_categories()
35+
ci.as_ordered()
36+
ci.as_unordered()
37+
times.append((time.perf_counter() - t0) * 1000)
38+
39+
total_ms = sum(times)
40+
mean_ms = total_ms / ITERATIONS
41+
print(json.dumps({
42+
"function": "categorical_index_modify",
43+
"mean_ms": mean_ms,
44+
"iterations": ITERATIONS,
45+
"total_ms": total_ms,
46+
}))
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Benchmark: pandas DataFrame.clip with Series bounds (axis=0) on 100k-row DataFrame.
3+
Outputs JSON: {"function": "clip_dataframe_with_bounds", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import numpy as np
8+
import pandas as pd
9+
10+
SIZE = 100_000
11+
WARMUP = 5
12+
ITERATIONS = 30
13+
14+
df = pd.DataFrame({
15+
"a": [(i % 200) - 100 for i in range(SIZE)],
16+
"b": [(i % 150) - 75 for i in range(SIZE)],
17+
"c": [(i % 100) - 50 for i in range(SIZE)],
18+
})
19+
20+
lower_bounds = pd.Series([(i % 40) - 20 for i in range(SIZE)])
21+
upper_bounds = pd.Series([(i % 40) + 20 for i in range(SIZE)])
22+
23+
for _ in range(WARMUP):
24+
df.clip(lower=lower_bounds, upper=upper_bounds, axis=0)
25+
26+
times = []
27+
for _ in range(ITERATIONS):
28+
t0 = time.perf_counter()
29+
df.clip(lower=lower_bounds, upper=upper_bounds, axis=0)
30+
times.append((time.perf_counter() - t0) * 1000)
31+
32+
total_ms = sum(times)
33+
mean_ms = total_ms / ITERATIONS
34+
print(json.dumps({
35+
"function": "clip_dataframe_with_bounds",
36+
"mean_ms": mean_ms,
37+
"iterations": ITERATIONS,
38+
"total_ms": total_ms,
39+
}))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Benchmark: pandas Series.clip with per-element Series bounds on 100k values.
3+
Outputs JSON: {"function": "clip_series_with_bounds", "mean_ms": ..., "iterations": ..., "total_ms": ...}
4+
"""
5+
import json
6+
import time
7+
import numpy as np
8+
import pandas as pd
9+
10+
SIZE = 100_000
11+
WARMUP = 5
12+
ITERATIONS = 30
13+
14+
data = [(i % 200) - 100 for i in range(SIZE)]
15+
lower = pd.Series([(i % 50) - 30 for i in range(SIZE)])
16+
upper = pd.Series([(i % 50) + 20 for i in range(SIZE)])
17+
series = pd.Series(data)
18+
19+
for _ in range(WARMUP):
20+
series.clip(lower=lower, upper=upper)
21+
22+
times = []
23+
for _ in range(ITERATIONS):
24+
t0 = time.perf_counter()
25+
series.clip(lower=lower, upper=upper)
26+
times.append((time.perf_counter() - t0) * 1000)
27+
28+
total_ms = sum(times)
29+
mean_ms = total_ms / ITERATIONS
30+
print(json.dumps({
31+
"function": "clip_series_with_bounds",
32+
"mean_ms": mean_ms,
33+
"iterations": ITERATIONS,
34+
"total_ms": total_ms,
35+
}))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Benchmark: pd.concat of multiple Series along axis=0 — vertical stacking
2+
of 5 Series of 20k elements each."""
3+
import json, time
4+
import numpy as np
5+
import pandas as pd
6+
7+
CHUNK = 20_000
8+
WARMUP = 5
9+
ITERATIONS = 30
10+
11+
s1 = pd.Series(np.arange(CHUNK, dtype=float) * 1.0)
12+
s2 = pd.Series(np.arange(CHUNK, dtype=float) * 2.0)
13+
s3 = pd.Series(np.arange(CHUNK, dtype=float) * 3.0)
14+
s4 = pd.Series(np.arange(CHUNK, dtype=float) * 4.0)
15+
s5 = pd.Series(np.arange(CHUNK, dtype=float) * 5.0)
16+
17+
for _ in range(WARMUP):
18+
pd.concat([s1, s2, s3, s4, s5])
19+
20+
start = time.perf_counter()
21+
for _ in range(ITERATIONS):
22+
pd.concat([s1, s2, s3, s4, s5])
23+
total = (time.perf_counter() - start) * 1000
24+
25+
print(json.dumps({
26+
"function": "concat_series_axis0",
27+
"mean_ms": total / ITERATIONS,
28+
"iterations": ITERATIONS,
29+
"total_ms": total,
30+
}))
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""
2+
Benchmark: pandas DataFrame.apply() — apply fn to each column (axis=0) and row (axis=1).
3+
Mirrors tsb's dataFrameApply (stats/apply.ts) behavior.
4+
Outputs JSON: {"function": "dataframe_apply_stats", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
"""
6+
import json
7+
import time
8+
import pandas as pd
9+
import numpy as np
10+
11+
SIZE = 10_000
12+
WARMUP = 3
13+
ITERATIONS = 20
14+
15+
df = pd.DataFrame({
16+
"a": (np.arange(SIZE) * 1.0),
17+
"b": (np.arange(SIZE) * 2.0),
18+
"c": (np.arange(SIZE) * 3.0),
19+
})
20+
21+
sum_fn = lambda col: col.mean() # noqa: E731
22+
23+
for _ in range(WARMUP):
24+
df.apply(sum_fn, axis=0)
25+
df.apply(sum_fn, axis=1)
26+
27+
times = []
28+
for _ in range(ITERATIONS):
29+
t0 = time.perf_counter()
30+
df.apply(sum_fn, axis=0)
31+
df.apply(sum_fn, axis=1)
32+
times.append((time.perf_counter() - t0) * 1000)
33+
34+
total_ms = sum(times)
35+
mean_ms = total_ms / ITERATIONS
36+
print(json.dumps({
37+
"function": "dataframe_apply_stats",
38+
"mean_ms": mean_ms,
39+
"iterations": ITERATIONS,
40+
"total_ms": total_ms,
41+
}))
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Benchmark: pandas DataFrame() construction — create 100k-row DataFrame from column arrays.
3+
Mirrors tsb's DataFrame.fromColumns() behavior.
4+
Outputs JSON: {"function": "dataframe_from_columns", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
"""
6+
import json
7+
import time
8+
import pandas as pd
9+
import numpy as np
10+
11+
SIZE = 100_000
12+
WARMUP = 5
13+
ITERATIONS = 30
14+
15+
col_a = np.arange(SIZE, dtype=float)
16+
col_b = np.arange(SIZE, dtype=float) * 2.5
17+
col_c = np.arange(SIZE) % 1000
18+
col_d = np.sin(np.arange(SIZE) * 0.001)
19+
20+
for _ in range(WARMUP):
21+
pd.DataFrame({"a": col_a, "b": col_b, "c": col_c, "d": col_d})
22+
23+
times = []
24+
for _ in range(ITERATIONS):
25+
t0 = time.perf_counter()
26+
pd.DataFrame({"a": col_a, "b": col_b, "c": col_c, "d": col_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": "dataframe_from_columns",
33+
"mean_ms": mean_ms,
34+
"iterations": ITERATIONS,
35+
"total_ms": total_ms,
36+
}))
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Benchmark: DataFrame column presence and access (.keys(), [], __getitem__) on 100k-row DataFrame."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 10
7+
ITERATIONS = 100
8+
9+
df = pd.DataFrame({"a": list(range(SIZE)), "b": [i * 2.0 for i in range(SIZE)], "c": [str(i) for i in range(SIZE)]})
10+
11+
for _ in range(WARMUP):
12+
"a" in df.columns
13+
df["b"]
14+
df.get("c")
15+
16+
times = []
17+
for _ in range(ITERATIONS):
18+
t0 = time.perf_counter()
19+
"a" in df.columns
20+
df["b"]
21+
df.get("c")
22+
times.append((time.perf_counter() - t0) * 1000)
23+
24+
total = sum(times)
25+
print(json.dumps({"function": "dataframe_has_col_get", "mean_ms": round(total / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total, 3)}))
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Benchmark: DataFrame.median() — column-wise median on 100k-row DataFrame."""
2+
import json, time
3+
import pandas as pd
4+
5+
SIZE = 100_000
6+
WARMUP = 5
7+
ITERATIONS = 50
8+
9+
df = pd.DataFrame({"a": [i * 1.1 for i in range(SIZE)], "b": [i * 2.2 for i in range(SIZE)], "c": [i * 3.3 for i in range(SIZE)]})
10+
11+
for _ in range(WARMUP): df.median()
12+
13+
times = []
14+
for _ in range(ITERATIONS):
15+
t0 = time.perf_counter()
16+
df.median()
17+
times.append((time.perf_counter() - t0) * 1000)
18+
19+
total = sum(times)
20+
print(json.dumps({"function": "dataframe_median_method", "mean_ms": round(total / ITERATIONS, 3), "iterations": ITERATIONS, "total_ms": round(total, 3)}))
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
Benchmark: pandas DataFrame.pipe with positional target argument on 100k-row DataFrame.
3+
Mirrors tsb's dataFramePipeTo — inserting the DataFrame at a specific arg position.
4+
Outputs JSON: {"function": "dataframe_pipe_to", "mean_ms": ..., "iterations": ..., "total_ms": ...}
5+
"""
6+
import json
7+
import time
8+
import numpy as np
9+
import pandas as pd
10+
11+
SIZE = 100_000
12+
WARMUP = 5
13+
ITERATIONS = 50
14+
15+
16+
def filter_above(threshold: float, df: pd.DataFrame) -> pd.DataFrame:
17+
return df[df["val"] > threshold]
18+
19+
20+
left = pd.DataFrame({
21+
"key": [i % 1000 for i in range(SIZE)],
22+
"val": [i * 1.5 for i in range(SIZE)],
23+
})
24+
25+
for _ in range(WARMUP):
26+
# pandas pipe with tuple form: (fn, 'positional_kwarg') — use pipe with lambda here
27+
left.pipe(lambda df: filter_above(50_000, df))
28+
29+
times = []
30+
for _ in range(ITERATIONS):
31+
t0 = time.perf_counter()
32+
left.pipe(lambda df: filter_above(50_000, df))
33+
times.append((time.perf_counter() - t0) * 1000)
34+
35+
total_ms = sum(times)
36+
mean_ms = total_ms / ITERATIONS
37+
print(json.dumps({
38+
"function": "dataframe_pipe_to",
39+
"mean_ms": mean_ms,
40+
"iterations": ITERATIONS,
41+
"total_ms": total_ms,
42+
}))

0 commit comments

Comments
 (0)