-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathsplit_bench.py
More file actions
93 lines (73 loc) · 2.63 KB
/
Copy pathsplit_bench.py
File metadata and controls
93 lines (73 loc) · 2.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""
Head-to-head ``split`` benchmark of PyPcre vs stdlib ``re`` and the ``regex`` package.
Run with:
python3 benchmarks/split_bench.py
Each workload splits 100,000 space-separated tokens. Times are the best of
several runs; lower is better. Compiled patterns are reused and PyPcre JIT is
enabled by default where applicable.
"""
from __future__ import annotations
import re as stdlib_re
import statistics
import sys
import time
from typing import Any
try:
import regex
except ImportError: # pragma: no cover - optional competitor
regex = None # type: ignore[assignment]
import pcre
def _best_ms(fn: Any, runs: int = 7) -> float:
times: list[float] = []
for _ in range(runs):
start = time.perf_counter()
fn()
times.append((time.perf_counter() - start) * 1000.0)
return min(times)
def _bench_split(label: str, pattern: Any, text: Any) -> dict[str, float | None]:
re_pat = stdlib_re.compile(pattern)
re_time = _best_ms(lambda: re_pat.split(text))
regex_time: float | None = None
if regex is not None:
regex_pat = regex.compile(pattern)
regex_time = _best_ms(lambda: regex_pat.split(text))
pc_pat = pcre.compile(pattern)
pc_time = _best_ms(lambda: pc_pat.split(text))
return {
"label": label,
"re": re_time,
"regex": regex_time,
"pcre": pc_time,
}
def main() -> int:
text = " ".join(f"w{i}" for i in range(100_000))
rows: list[dict[str, Any]] = [
_bench_split("Delimiter no group", r"\s+", text),
_bench_split("Delimiter with group", r"(\s+)", text),
_bench_split("Single char", r" ", text),
_bench_split("Single char with group", r"( )", text),
_bench_split("Empty pattern", r"", text),
]
print("\n| Workload | re (ms) | regex (ms) | PyPcre (ms) | edge vs re | edge vs regex |")
print("| --- | ---: | ---: | ---: | ---: | ---: |")
for row in rows:
re_t = row["re"]
regex_t = row["regex"]
pc_t = row["pcre"]
vs_re = f"{re_t / pc_t:.2f}x" if pc_t else "n/a"
vs_regex = f"{regex_t / pc_t:.2f}x" if regex_t and pc_t else "n/a"
regex_cell = f"{regex_t:.3f}" if regex_t is not None else "-"
print(
f"| {row['label']} | {re_t:.3f} | {regex_cell} | "
f"{pc_t:.3f} | {vs_re} | {vs_regex} |"
)
winners = [
r
for r in rows
if r["pcre"]
and (r["re"] / r["pcre"] > 2.0 or (r["regex"] and r["regex"] / r["pcre"] > 2.0))
]
print(f"\nPyPcre is >2x faster on {len(winners)} of {len(rows)} workloads.")
return 0
if __name__ == "__main__":
sys.exit(main())