feat(convert): integrate process metrics into JSON and SQLite converters
- add process_metrics function to record peak RSS and CPU time - update JSON converter to include metrics in completion marker - modify SQLite converter to utilize new metrics for performance tracking - enhance storage size updates with locking mechanism for concurrent access
This commit is contained in:
73
common/results.py
Normal file
73
common/results.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""Canonical benchmark result handling (spec 08, test-pipeline-validation).
|
||||
|
||||
Every implementation of a benchmark query returns the same canonical rows:
|
||||
tuples of str / int / float columns. Rows are compared SORTED; floats agree
|
||||
within 1e-9. The canonical expected artifacts are produced from PostgreSQL
|
||||
and cross-validated against SQLite (task 08); every measured run (task 09)
|
||||
re-validates its rows against them.
|
||||
|
||||
Two serializations:
|
||||
- full: floats via repr() - lossless, used for stdout and expected/*.rows.csv;
|
||||
- rounded (6 decimals): used ONLY for the sha256 fingerprint, so engines
|
||||
whose aggregate arithmetic differs in the last bits hash identically.
|
||||
The tolerant row comparison is the authoritative check; the sha256 is a
|
||||
compact fingerprint for reports and markers.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import sys
|
||||
|
||||
from common.corpus import coerce
|
||||
|
||||
FLOAT_TOL = 1e-9
|
||||
|
||||
Row = tuple
|
||||
|
||||
|
||||
def sort_rows(rows: list[Row]) -> list[Row]:
|
||||
return sorted(rows)
|
||||
|
||||
|
||||
def _fmt(value, rounded: bool) -> str:
|
||||
if isinstance(value, float):
|
||||
return f"{round(value, 6):.6f}" if rounded else repr(value)
|
||||
return str(value)
|
||||
|
||||
|
||||
def serialize(rows: list[Row], rounded: bool = False) -> str:
|
||||
return "\n".join(",".join(_fmt(v, rounded) for v in row) for row in rows) + "\n"
|
||||
|
||||
|
||||
def sha256_of(rows: list[Row]) -> str:
|
||||
return hashlib.sha256(serialize(sort_rows(rows), rounded=True).encode("ascii")).hexdigest()
|
||||
|
||||
|
||||
def print_rows(rows: list[Row]) -> None:
|
||||
sys.stdout.write(serialize(sort_rows(rows)))
|
||||
|
||||
|
||||
def parse_rows(text: str) -> list[Row]:
|
||||
rows = []
|
||||
for line in text.splitlines():
|
||||
if line:
|
||||
rows.append(tuple(coerce(cell) for cell in line.split(",")))
|
||||
return rows
|
||||
|
||||
|
||||
def compare(expected: list[Row], actual: list[Row], tol: float = FLOAT_TOL) -> str | None:
|
||||
"""Return None when equal within tolerance, else a first-difference message."""
|
||||
exp, act = sort_rows(expected), sort_rows(actual)
|
||||
if len(exp) != len(act):
|
||||
return f"row count {len(act)} != expected {len(exp)}"
|
||||
for i, (er, ar) in enumerate(zip(exp, act)):
|
||||
if len(er) != len(ar):
|
||||
return f"row {i}: arity {len(ar)} != {len(er)}"
|
||||
for j, (ev, av) in enumerate(zip(er, ar)):
|
||||
if isinstance(ev, float) or isinstance(av, float):
|
||||
if abs(float(ev) - float(av)) > tol:
|
||||
return f"row {i} col {j}: {av!r} != {ev!r} (tol {tol})"
|
||||
elif ev != av:
|
||||
return f"row {i} col {j}: {av!r} != {ev!r}"
|
||||
return None
|
||||
Reference in New Issue
Block a user