-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathgenerate_tables.py
More file actions
126 lines (105 loc) · 3.59 KB
/
Copy pathgenerate_tables.py
File metadata and controls
126 lines (105 loc) · 3.59 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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python3
"""Generate markdown tables from benchmark results."""
import re
import math
from collections import defaultdict
OP_LABELS = {
"A": "Insert: Single",
"B": "Insert: Batch",
"C": "Insert: Bulk",
"D": "Filter: Large",
"E": "Filter: Small",
"F": "Get",
"G": "Filter: dict",
"H": "Filter: tuple",
"I": "Update: Whole",
"J": "Update: Partial",
"K": "Delete",
}
ORM_ORDER = ["Django", "peewee", "SA ORM async", "SQLObject", "Tortoise ORM", "Piccolo", "ormar", "SQLModel"]
OP_ORDER = list("ABCDEFGHIJK")
def parse_results(filepath):
data = defaultdict(dict)
with open(filepath) as f:
for line in f:
m = re.match(r"^\s*(?:\d+[→\t])?\s*(.+?),\s*([A-K]):\s*Rows/sec:\s*([\d.]+)", line)
if m:
orm, op, val = m.group(1).strip(), m.group(2), float(m.group(3))
data[orm][op] = val
return dict(data)
def compute_geometric_mean(values):
vals = [v for v in values if v > 0]
if not vals:
return 0
return math.exp(sum(math.log(v) for v in vals) / len(vals))
def fmt(val):
if val == 0:
return "—"
return f"{val:,.0f}"
def generate_table(data, orms=None):
if orms is None:
orms = [o for o in ORM_ORDER if o in data]
# Find best ORM for each operation
best = {}
for op in OP_ORDER:
max_val = 0
max_orm = ""
for orm in orms:
v = data.get(orm, {}).get(op, 0)
if v > max_val:
max_val = v
max_orm = orm
best[op] = max_orm
# Geometric means
gm = {}
for orm in orms:
vals = [data.get(orm, {}).get(op, 0) for op in OP_ORDER]
gm[orm] = compute_geometric_mean([v for v in vals if v > 0])
best_gm_orm = max(gm, key=lambda o: gm[o])
header = "| Operation |" + "|".join(f" {o} " for o in orms) + "| Best |"
sep = "|-----------|" + "|".join("-" * (len(o) + 2) for o in orms) + "|------|"
lines = [header, sep]
for op in OP_ORDER:
row = f"| {OP_LABELS[op]} |"
for orm in orms:
v = data.get(orm, {}).get(op, 0)
cell = fmt(v)
if orm == best[op] and v > 0:
cell = f"**{cell}**"
row += f" {cell} |"
row += f" {best[op]} |"
lines.append(row)
# Geometric mean row
row = "| **Geometric Mean** |"
for orm in orms:
cell = fmt(gm[orm])
if orm == best_gm_orm:
cell = f"**{cell}**"
row += f" {cell} |"
row += f" **{best_gm_orm}** |"
lines.append(row)
return "\n".join(lines)
# PostgreSQL
print("### PostgreSQL\n")
for i, test_name in enumerate(["Test 1: Simple Model (4 fields)", "Test 2: FK Relations", "Test 3: Wide Model (32+ fields)"], 1):
data = parse_results(f"/tmp/pg_outfile{i}")
orms = [o for o in ORM_ORDER if o in data]
print(f"#### {test_name}\n")
print(generate_table(data, orms))
print()
# MySQL
print("\n### MySQL\n")
for i, test_name in enumerate(["Test 1: Simple Model (4 fields)", "Test 2: FK Relations", "Test 3: Wide Model (32+ fields)"], 1):
data = parse_results(f"/tmp/mysql_outfile{i}")
orms = [o for o in ORM_ORDER if o in data]
print(f"#### {test_name}\n")
print(generate_table(data, orms))
print()
# SQLite
print("\n### SQLite\n")
for i, test_name in enumerate(["Test 1: Simple Model (4 fields)", "Test 2: FK Relations", "Test 3: Wide Model (32+ fields)"], 1):
data = parse_results(f"/tmp/sqlite_outfile{i}")
orms = [o for o in ORM_ORDER if o in data]
print(f"#### {test_name}\n")
print(generate_table(data, orms))
print()