@@ -74,6 +74,13 @@ def render_perf_markdown(results: PerfResults, path: Path) -> None:
7474 lines .append (f"*Profile: { data ['metadata' ]['profile' ]} *" )
7575 lines .append (f"*Platform: { data ['metadata' ]['platform' ]} *" )
7676 lines .append (f"*Python: { data ['metadata' ]['python' ]} *" )
77+ run_env = data ["metadata" ].get ("run_environment" ) or {}
78+ if run_env :
79+ lines .append (f"*CPU: { run_env .get ('cpu_model' ) or 'unknown' } *" )
80+ lines .append (
81+ f"*Cores: { run_env .get ('core_count' ) or 'unknown' } | "
82+ f"Memory MB: { run_env .get ('memory_total_mb' ) or 'unknown' } *"
83+ )
7784 if data ["metadata" ].get ("commit" ):
7885 lines .append (f"*Commit: { data ['metadata' ]['commit' ]} *" )
7986 cfg = data ["metadata" ].get ("config" , {})
@@ -93,6 +100,10 @@ def render_perf_markdown(results: PerfResults, path: Path) -> None:
93100 "These numbers measure only the library under test. "
94101 "Write timings do NOT include oracle verification."
95102 )
103+ lines .append (
104+ "Confidence note: treat deltas under ~5% as noise unless "
105+ "stable across multiple runs."
106+ )
96107 lines .append ("" )
97108
98109 workload_features = _collect_workload_features (libs , features , lookup )
@@ -107,10 +118,10 @@ def render_perf_markdown(results: PerfResults, path: Path) -> None:
107118 for lib in libs :
108119 caps = set (data ["libraries" ][lib ].get ("capabilities" , []))
109120 if "read" in caps :
110- header += f" { lib } (R p50 ms) |"
121+ header += f" { lib } (R p50/p95 ms) |"
111122 sep += "--------------|"
112123 if "write" in caps :
113- header += f" { lib } (W p50 ms) |"
124+ header += f" { lib } (W p50/p95 ms) |"
114125 sep += "--------------|"
115126
116127 tier_map : dict [int , list [str ]] = {0 : [], 1 : [], 2 : []}
@@ -133,9 +144,9 @@ def render_perf_markdown(results: PerfResults, path: Path) -> None:
133144 entry = lookup .get ((feat , lib ))
134145 perf = entry .get ("perf" ) if entry else None
135146 if "read" in caps :
136- row += f" { _fmt_p50_ms (perf , 'read' )} |"
147+ row += f" { _fmt_p50_p95_ms (perf , 'read' )} |"
137148 if "write" in caps :
138- row += f" { _fmt_p50_ms (perf , 'write' )} |"
149+ row += f" { _fmt_p50_p95_ms (perf , 'write' )} |"
139150 lines .append (row )
140151 lines .append ("" )
141152
@@ -303,7 +314,7 @@ def _fmt_rate(rate: float) -> str:
303314 return f"{ rate :.2f} "
304315
305316
306- def _fmt_p50_ms (perf : dict [str , Any ] | None , op : str ) -> str :
317+ def _fmt_p50_p95_ms (perf : dict [str , Any ] | None , op : str ) -> str :
307318 if not perf or not isinstance (perf , dict ):
308319 return "—"
309320 op_data = perf .get (op )
@@ -316,16 +327,37 @@ def _fmt_p50_ms(perf: dict[str, Any] | None, op: str) -> str:
316327 if p50 is None :
317328 return "—"
318329 try :
319- return f"{ float (p50 ):.2f} "
330+ p95 = wall .get ("p95" )
331+ p95_txt = f"/{ float (p95 ):.2f} " if p95 is not None else ""
332+ return f"{ float (p50 ):.2f} { p95_txt } "
320333 except (TypeError , ValueError ):
321334 return "—"
322335
323336
324337def render_perf_csv (results : PerfResults , path : Path ) -> None :
325338 data = perf_results_to_json_dict (results )
339+ history_path = path .parent / "history.jsonl"
340+ history_entries = _load_matching_history_entries (data , history_path )
341+ header_columns = [
342+ "library" ,
343+ "feature" ,
344+ "read_p50_wall_ms" ,
345+ "read_p95_wall_ms" ,
346+ "read_op_count" ,
347+ "read_op_unit" ,
348+ "read_p50_units_per_sec" ,
349+ "write_p50_wall_ms" ,
350+ "write_p95_wall_ms" ,
351+ "write_op_count" ,
352+ "write_op_unit" ,
353+ "write_p50_units_per_sec" ,
354+ "read_tail_ratio" ,
355+ "write_tail_ratio" ,
356+ "confidence_note" ,
357+ "regression_status" ,
358+ ]
326359 lines = [
327- "library,feature,read_p50_wall_ms,read_p95_wall_ms,read_op_count,read_op_unit,read_p50_units_per_sec,"
328- "write_p50_wall_ms,write_p95_wall_ms,write_op_count,write_op_unit,write_p50_units_per_sec" ,
360+ "," .join (header_columns ),
329361 ]
330362 for r in data ["results" ]:
331363 perf = r .get ("perf" ) or {}
@@ -350,6 +382,9 @@ def _rate(count: Any, p50_ms: Any) -> str:
350382 def _f (v : Any ) -> str :
351383 return "" if v is None else str (v )
352384
385+ read_tail_ratio = _tail_ratio (read_wall )
386+ write_tail_ratio = _tail_ratio (write_wall )
387+ reg_status = _regression_status (history_entries , r )
353388 lines .append (
354389 "," .join (
355390 [
@@ -365,6 +400,17 @@ def _f(v: Any) -> str:
365400 _f (write_count ),
366401 _f (write_unit ),
367402 _rate (write_count , write_wall .get ("p50" )),
403+ _f (read_tail_ratio ),
404+ _f (write_tail_ratio ),
405+ _f (
406+ "high"
407+ if (
408+ (read_tail_ratio or 0 ) > 0.20
409+ or (write_tail_ratio or 0 ) > 0.20
410+ )
411+ else "ok"
412+ ),
413+ reg_status ,
368414 ]
369415 )
370416 )
@@ -400,3 +446,73 @@ def append_perf_history(results: PerfResults, history_path: Path) -> None:
400446
401447 with open (history_path , "a" ) as f :
402448 f .write (json .dumps (entry ) + "\n " )
449+
450+
451+ def _tail_ratio (wall : dict [str , Any ]) -> float | None :
452+ p50 = wall .get ("p50" )
453+ p95 = wall .get ("p95" )
454+ try :
455+ if p50 is None or p95 is None :
456+ return None
457+ p50_float = float (p50 )
458+ if p50_float == 0 :
459+ return None
460+ return round (max (float (p95 ) - p50_float , 0.0 ) / p50_float , 4 )
461+ except (TypeError , ValueError , ZeroDivisionError ):
462+ return None
463+
464+
465+ def _load_matching_history_entries (
466+ data : dict [str , Any ],
467+ history_path : Path ,
468+ ) -> list [dict [str , Any ]]:
469+ if not history_path .exists ():
470+ return []
471+ metadata = data .get ("metadata" , {})
472+ current_profile = metadata .get ("profile" )
473+ current_config = metadata .get ("config" )
474+ entries : list [dict [str , Any ]] = []
475+ for line in history_path .read_text ().splitlines ():
476+ try :
477+ entry = json .loads (line )
478+ if entry .get ("profile" ) != current_profile or entry .get ("config" ) != current_config :
479+ continue
480+ entries .append (entry )
481+ except json .JSONDecodeError :
482+ continue
483+ return entries
484+
485+
486+ def _regression_status (
487+ history_entries : list [dict [str , Any ]],
488+ row : dict [str , Any ],
489+ * ,
490+ threshold_pct : float = 10.0 ,
491+ ) -> str :
492+ if not history_entries :
493+ return "no_history"
494+ vals : list [float ] = []
495+ for entry in history_entries :
496+ try :
497+ sample = entry .get ("p50_wall_ms" , {}).get (row ["library" ], {}).get (row ["feature" ], {})
498+ rv = sample .get ("read_p50" )
499+ if rv is not None :
500+ vals .append (float (rv ))
501+ except (TypeError , ValueError , KeyError ):
502+ continue
503+ if len (vals ) < 3 :
504+ return "insufficient_history"
505+ baseline = sorted (vals [- 5 :])[len (vals [- 5 :]) // 2 ]
506+ cur = ((row .get ("perf" ) or {}).get ("read" ) or {}).get ("wall_ms" , {}).get ("p50" )
507+ try :
508+ curf = float (cur )
509+ except (TypeError , ValueError ):
510+ return "n/a"
511+ if baseline <= 0 :
512+ return "n/a"
513+ delta = ((curf - baseline ) / baseline ) * 100.0
514+ if delta > threshold_pct :
515+ return f"regressed:{ delta :.1f} %"
516+ if delta < - threshold_pct :
517+ return f"improved:{ delta :.1f} %"
518+ return f"stable:{ delta :.1f} %"
0 commit comments