2626
2727 - name : Install deps
2828 run : |
29- python -m pip install --quiet --upgrade wandb
29+ python -m pip install --quiet --upgrade wandb frozendict
3030
3131 - name : Download artifacts from triggering run
3232 id : dl
7171
7272 import os, sys, json, re, math, statistics
7373 import wandb
74+ from frozendict import frozendict
7475 from pathlib import Path
7576 import csv
7677
@@ -98,23 +99,44 @@ jobs:
9899
99100 METRIC_KEYS = ("compile_time", "runtime_fps", "realtime_factor")
100101
101- def _normalize_kv_id(kv: dict) -> str:
102- return "-".join(f"{k}={v}" for k, v in sorted(kv.items()))
103-
104- def normalize_benchmark_id(bid: str) -> str:
105- kv = dict(map(str.strip, token.split("=", 1)) for token in bid.split("-"))
106- return _normalize_kv_id(kv)
107-
108- def parse_norm_id(nbid: str) -> dict:
102+ def parse_benchmark_id(bid: str) -> dict:
109103 kv = {}
110- if nbid :
111- for token in nbid .split("-"):
104+ if bid :
105+ for token in bid .split("-"):
112106 token = token.strip()
113107 if token and "=" in token:
114108 k, v = token.split("=", 1)
115109 kv[k.strip()] = v.strip()
116110 return kv
117111
112+ def normalize_benchmark_id(bid: str) -> frozendict[str, str]:
113+ return frozendict(parse_benchmark_id(bid))
114+
115+ def get_param_names(bids: tuple[frozendict]) -> tuple[str, ...]:
116+ """
117+ Merge a list of tuples into a single tuple of keys that:
118+ - Preserves the relative order of keys within each tuple
119+ - Gives precedence to later tuples when conflicts arise
120+ """
121+ merged = list(bids[-1])
122+ merged_set = set(merged)
123+ for tup in bids[:-1]:
124+ for key in tup:
125+ if key not in merged_set:
126+ merged.append(key)
127+ merged_set.add(key)
128+ return tuple(merged)
129+
130+ def sort_key(d):
131+ key_list = []
132+ for col in params_name:
133+ if col in d:
134+ val = d[col]
135+ key_list.append((0, val))
136+ else:
137+ key_list.append((1, None))
138+ return key_list
139+
118140 def artifacts_parse_csv_summary(current_txt_path):
119141 out = {}
120142 for line in current_txt_path.read_text().splitlines():
@@ -125,8 +147,8 @@ jobs:
125147 record[k] = float(kv.pop(k))
126148 except (ValueError, TypeError, KeyError):
127149 pass
128- bid = _normalize_kv_id (kv)
129- out[bid ] = record
150+ nbid = frozendict (kv)
151+ out[nbid ] = record
130152 return out
131153
132154 def fmt_num(v, is_int: bool):
@@ -143,7 +165,7 @@ jobs:
143165 current_bm = {}
144166 for csv_path in current_csv_paths:
145167 current_bm |= artifacts_parse_csv_summary(csv_path)
146- bids_set = set (current_bm.keys())
168+ bids_set = frozenset (current_bm.keys())
147169 assert bids_set
148170
149171 # ----- W&B baselines -----
@@ -199,7 +221,6 @@ jobs:
199221 # Extract benchmark ID and normalize it to make sure it does not depends on key ordering.
200222 # Note that the rigid body benchmark suite is the only one being supported for now.
201223 sid, bid = config["benchmark_id"].split("-", 1)
202- nbid = normalize_benchmark_id(bid)
203224 if sid != "rigid_body":
204225 continue
205226
@@ -217,15 +238,15 @@ jobs:
217238 continue
218239
219240 # Store all the records into a dict
241+ nbid = normalize_benchmark_id(bid)
220242 records_by_rev.setdefault(rev, {})[nbid] = {
221243 metric: summary[metric] for metric in METRIC_KEYS
222244 }
223245
224246 # ----- build TWO tables -----
225247
226- # Parse benchmark IDs into key-value dicts
227- params_map_by_bid = {bid: parse_norm_id(bid) for bid in current_bm.keys()}
228- params_name = sorted(set(e for kv in params_map_by_bid.values() for e in kv.keys()))
248+ # Parse benchmark IDs into key-value dicts while preserving order
249+ params_name = get_param_names(tuple((tuple(kv.keys())) for kv in current_bm.keys()))
229250
230251 reg_found = False
231252 tables = {}
@@ -244,13 +265,12 @@ jobs:
244265 header = "| " + " | ".join(header_cells) + " |"
245266 align = "|:------:|" + "|".join([":---" for _ in params_name]) + "|---:|---:|---:|"
246267
247- for bid in sorted(current_bm.keys()):
268+ for bid in sorted(current_bm.keys(), key=sort_key ):
248269 value_cur = current_bm[bid][metric]
249270 is_int = isinstance(value_cur, int) or value_cur.is_integer()
250271 value_repr = fmt_num(value_cur, is_int)
251272
252- params_map = params_map_by_bid[bid]
253- params_repr = [params_map.get(k, "-") for k in params_name]
273+ params_repr = [bid.get(k, "-") for k in params_name]
254274 info = {
255275 **dict(zip(params_name, params_repr)),
256276 "current": value_cur,
@@ -382,6 +402,16 @@ jobs:
382402 echo "CONCLUSION=$([ "$EXIT_CODE" = "0" ] && echo 'success' || echo 'failure')" >> "$GITHUB_ENV"
383403 echo "HAS_REGRESSIONS=$([ "$EXIT_CODE" = "$EXIT_CODE_REGRESSION" ] && echo 1 || echo 0)" >> "$GITHUB_ENV"
384404
405+ - name : Upload benchmark comparisons in CSV
406+ id : upload
407+ uses : actions/upload-artifact@v4
408+ with :
409+ name : benchmark-comparison-tables
410+ path : |
411+ runtime_fps.csv
412+ compile_time.csv
413+ if-no-files-found : warn
414+
385415 - name : Add PR comment
386416 if : ${{ env.SCRIPT_OUTPUT != '' }}
387417 uses : actions/github-script@v8
@@ -409,15 +439,21 @@ jobs:
409439 });
410440
411441 - name : Publish PR check
412- if : always()
413442 uses : actions/github-script@v8
414443 env :
415444 CHECK_NAME : Benchmark Comparison
416- CHECK_BODY : ${{ env.CHECK_OUTPUT }}
445+ CHECK_OUTPUT : ${{ env.CHECK_OUTPUT }}
417446 CONCLUSION : ${{ env.CONCLUSION }}
418447 HAS_REGRESSIONS : ${{ env.HAS_REGRESSIONS }}
448+ ARTIFACT_URL : ${{ steps.upload.outputs.artifact-url }}
419449 with :
420450 script : |
451+ const artifactUrl = process.env.ARTIFACT_URL || '';
452+ let body = process.env.CHECK_OUTPUT || '';
453+ if (body && artifactUrl) {
454+ body += `\n\n**Artifact:** [Download raw data](${artifactUrl})`;
455+ }
456+
421457 const summary = (process.env.HAS_REGRESSIONS || '0') === '1'
422458 ? '🔴 Regressions detected. See tables below.'
423459 : '✅ No regressions detected. See tables below.';
@@ -431,16 +467,6 @@ jobs:
431467 output: {
432468 title: process.env.CHECK_NAME,
433469 summary,
434- text: process.env.CHECK_BODY || undefined
470+ text: body || undefined
435471 }
436472 });
437-
438- - name : Upload benchmark comparisons in CSV & JSONL
439- if : always()
440- uses : actions/upload-artifact@v4
441- with :
442- name : benchmark-comparison-tables
443- path : |
444- runtime_fps.csv
445- compile_time.csv
446- if-no-files-found : warn
0 commit comments