Skip to content

Commit 82aa9ca

Browse files
committed
Add reproducible native Vortex and Parquet comparison with round-trip contracts
1 parent 50cff19 commit 82aa9ca

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""Native Vortex/Parquet benchmark; deterministic non-sensitive synthetic test data.
2+
3+
State contract: DISCOVERED -> ENCODED -> ROUNDTRIP_VERIFIED -> BENCHMARKED -> REPORTED.
4+
A format is not rankable if any step fails. This CI test does not replace
5+
the 1,876-row user research catalogue benchmark distributed separately.
6+
"""
7+
import csv
8+
import hashlib
9+
import json
10+
import os
11+
from pathlib import Path
12+
import platform
13+
import statistics
14+
import sys
15+
import tempfile
16+
import time
17+
18+
import brotli
19+
import pyarrow as pa
20+
import pyarrow.parquet as pq
21+
import vortex as vx
22+
import vortex.expr as ve
23+
24+
OUT = Path("web_atomic_native_results")
25+
OUT.mkdir(exist_ok=True)
26+
ROWS = [
27+
{"record_id":f"atom_{i:07d}", "kind":("HTML", "SVG", "MathML", "JSON")[i % 4],
28+
"element":("div", "path", "mfrac", "object", "span")[i % 5],
29+
"algorithm":("tree", "dictionary", "delta")[i % 3], "metric":str(i % 101)}
30+
for i in range(1876)
31+
]
32+
COLS = list(ROWS[0])
33+
TARGET = ROWS[len(ROWS)//2]["record_id"]
34+
FORMATS = ("column_json_brotli", "parquet_zstd", "vortex_default", "vortex_compact")
35+
OPS = ("full_read", "one_column", "id_lookup")
36+
37+
38+
def logical_hash(rows, cols):
39+
values = [[r[c] for c in cols] for r in rows]
40+
return hashlib.sha256(json.dumps(values, separators=(",", ":")).encode()).hexdigest()
41+
42+
43+
def store(fmt, path):
44+
if fmt == "column_json_brotli":
45+
cols = {c: [row[c] for row in ROWS] for c in COLS}
46+
path.write_bytes(brotli.compress(json.dumps(cols, separators=(",", ":")).encode(), quality=5))
47+
elif fmt == "parquet_zstd":
48+
pq.write_table(pa.table({c:pa.array([r[c] for r in ROWS],type=pa.string()) for c in COLS}),
49+
str(path), compression="zstd", use_dictionary=True)
50+
else:
51+
array = vx.array(ROWS)
52+
if fmt == "vortex_default":
53+
vx.io.write(array, str(path))
54+
else:
55+
vx.io.VortexWriteOptions.compact().write(array, str(path))
56+
57+
58+
def load(fmt, path, op):
59+
if fmt == "column_json_brotli":
60+
cols = json.loads(brotli.decompress(path.read_bytes()))
61+
indices = range(len(ROWS))
62+
if op == "id_lookup":
63+
indices = (i for i, x in enumerate(cols["record_id"]) if x == TARGET)
64+
keys = ["record_id"] if op == "one_column" else COLS
65+
return [{c:cols[c][i] for c in keys} for i in indices]
66+
if fmt == "parquet_zstd":
67+
return pq.read_table(str(path), columns=["record_id"] if op == "one_column" else None,
68+
filters=[("record_id", "==", TARGET)] if op == "id_lookup" else None).to_pylist()
69+
handle = vx.open(str(path))
70+
projection = ["record_id"] if op == "one_column" else None
71+
expr = (ve.column("record_id") == TARGET) if op == "id_lookup" else None
72+
return handle.scan(projection, expr=expr).read_all().to_pylist()
73+
74+
75+
def measure(fn):
76+
for _ in range(3):
77+
fn()
78+
values=[]
79+
for _ in range(12):
80+
t=time.perf_counter_ns()
81+
fn()
82+
values.append((time.perf_counter_ns()-t)/1e6)
83+
return round(statistics.median(values), 6)
84+
85+
86+
records = []
87+
failure = False
88+
for fmt in FORMATS:
89+
entry={"format":fmt, "input_type":"deterministic_synthetic", "rows":len(ROWS),
90+
"state":"DISCOVERED", "bytes":None, "write_ms":None, "error":""}
91+
try:
92+
with tempfile.TemporaryDirectory() as folder:
93+
path=Path(folder)/("data."+fmt)
94+
start=time.perf_counter_ns()
95+
store(fmt, path)
96+
entry["write_ms"]=round((time.perf_counter_ns()-start)/1e6,6)
97+
entry["bytes"]=path.stat().st_size
98+
entry["state"]="ENCODED"
99+
assert logical_hash(load(fmt,path,"full_read"),COLS)==logical_hash(ROWS,COLS)
100+
entry["state"]="ROUNDTRIP_VERIFIED"
101+
for op in OPS:
102+
expect = ROWS if op=="full_read" else (
103+
[{"record_id":r["record_id"]} for r in ROWS] if op=="one_column" else
104+
[r for r in ROWS if r["record_id"]==TARGET])
105+
cols = ["record_id"] if op=="one_column" else COLS
106+
assert logical_hash(load(fmt,path,op),cols)==logical_hash(expect,cols)
107+
entry[op+"_median_ms"]=measure(lambda:load(fmt,path,op))
108+
entry["state"]="BENCHMARKED"
109+
except Exception as e:
110+
failure=True
111+
entry["state"]="FAILED"
112+
entry["error"]=type(e).__name__+": "+str(e)
113+
records.append(entry)
114+
print(json.dumps(entry))
115+
metadata={"python":sys.version,"platform":platform.platform(),"pyarrow":pa.__version__,
116+
"vortex":getattr(vx,"__version__","unreported"),"input_sha256":logical_hash(ROWS,COLS),
117+
"repeats":12,"warmups":3}
118+
(OUT/"environment.json").write_text(json.dumps(metadata,indent=2))
119+
(OUT/"measurements.json").write_text(json.dumps(records,indent=2))
120+
with (OUT/"measurements.tsv").open("w") as fp:
121+
keys=["format","input_type","rows","state","bytes","write_ms","full_read_median_ms",
122+
"one_column_median_ms","id_lookup_median_ms","error"]
123+
fp.write("\t".join(keys)+"\n")
124+
for rec in records:
125+
fp.write("\t".join(str(rec.get(k,"")) for k in keys)+"\n")
126+
sys.exit(1 if failure else 0)

0 commit comments

Comments
 (0)