@@ -51,7 +51,7 @@ def get_machines(results: Iterable[mod_result.Result]) -> set[str]:
5151
5252def compare_pair (
5353 output_dir : PathLike ,
54- machine : str ,
54+ machine : str | None ,
5555 ref_name : str ,
5656 ref : mod_result .Result ,
5757 head_name : str ,
@@ -60,11 +60,17 @@ def compare_pair(
6060) -> str :
6161 output_dir = Path (output_dir )
6262
63- rich .print (f"Comparing { counter [0 ]+ 1 } /{ counter [1 ]} " , end = "\r " )
63+ rich .print (
64+ f"Comparing { counter [0 ]+ 1 : 2} /{ counter [1 ]: 2} : { head_name } vs. { ref_name } "
65+ )
6466 counter [0 ] += 1
6567
66- name = f"{ machine } -{ head_name } -vs-{ ref_name } "
67- comparison = mod_result .BenchmarkComparison (ref , head , "base" )
68+ name_parts = []
69+ if machine is not None :
70+ name_parts .append (machine )
71+ name_parts .extend ([head_name , "vs" , ref_name ])
72+ name = "-" .join (name_parts )
73+ comparison = mod_result .BenchmarkComparison (ref , head , "base" , True )
6874 entry = [comparison .summary ]
6975 for func , suffix , file_type in comparison .get_files ():
7076 output_filename = util .apply_suffix (output_dir / name , suffix )
@@ -78,46 +84,56 @@ def write_row(fd: TextIO, columns: Iterable[str]):
7884 fd .write (f"| { ' | ' .join (columns )} |\n " )
7985
8086
87+ def name_and_hash (name : str , hash : str ) -> str :
88+ if name == hash :
89+ return name
90+ return f"{ name } ({ hash } )"
91+
92+
93+ def get_first_result_for_machine (
94+ results : Iterable [mod_result .Result ], machine : str | None
95+ ) -> mod_result .Result :
96+ return next (
97+ result for result in results if machine is None or result .nickname == machine
98+ )
99+
100+
81101def do_one_to_many (
82102 fd : TextIO ,
83103 parsed_commits : ParsedCommits ,
84- machine : str ,
104+ machine : str | None ,
85105 output_dir : PathLike ,
86106 counter : list [int ],
87107) -> None :
88108 _ , _ , first_name , first_results = parsed_commits [0 ]
89- first_result = next (
90- result for result in first_results if result .nickname == machine
91- )
109+ first_result = get_first_result_for_machine (first_results , machine )
92110 write_row (fd , ["commit" , "change" ])
93111 write_row (fd , ["--" ] * 2 )
94112 for hash , _ , name , results in parsed_commits [1 :]:
95- result = next ( result for result in results if result . nickname == machine )
113+ result = get_first_result_for_machine ( results , machine )
96114 link = compare_pair (
97115 output_dir , machine , first_name , first_result , name , result , counter
98116 )
99- write_row (fd , [f" { name } ( { hash } )" , link ])
117+ write_row (fd , [name_and_hash ( name , hash ) , link ])
100118
101119
102120def do_many_to_many (
103121 fd ,
104122 parsed_commits : ParsedCommits ,
105- machine : str ,
123+ machine : str | None ,
106124 output_dir : PathLike ,
107125 counter : list [int ],
108126) -> None :
109- write_row (fd , ["" , * [f" { x [2 ]} ( { x [0 ]} )" for x in parsed_commits ]])
127+ write_row (fd , ["" , * [name_and_hash ( x [2 ], x [0 ]) for x in parsed_commits ]])
110128 write_row (fd , ["--" ] * (len (parsed_commits ) + 1 ))
111129 for hash1 , flags1 , name1 , results1 in parsed_commits :
112130 columns = [name1 ]
113- result1 = next ( result for result in results1 if result . nickname == machine )
131+ result1 = get_first_result_for_machine ( results1 , machine )
114132 for hash2 , flags2 , name2 , results2 in parsed_commits :
115133 if hash1 == hash2 and flags1 == flags2 :
116134 columns .append ("" )
117135 else :
118- result2 = next (
119- result for result in results2 if result .nickname == machine
120- )
136+ result2 = get_first_result_for_machine (results2 , machine )
121137 link = compare_pair (
122138 output_dir , machine , name1 , result1 , name2 , result2 , counter
123139 )
@@ -127,14 +143,11 @@ def do_many_to_many(
127143 fd .write ("\n \n Rows are 'bases', columns are 'heads'\n " )
128144
129145
130- def _main (commits : Sequence [str ], output_dir : PathLike , comparison_type : str ):
146+ def _main_with_hashes (commits : Sequence [str ], output_dir : Path , comparison_type : str ):
131147 results = mod_result .load_all_results (
132148 None , Path ("results" ), sorted = False , match = False
133149 )
134150
135- if len (commits ) < 2 :
136- raise ValueError ("Must provide at least 2 commits" )
137-
138151 parsed_commits = []
139152 machines = set ()
140153
@@ -163,10 +176,6 @@ def _main(commits: Sequence[str], output_dir: PathLike, comparison_type: str):
163176 if len (machines ) == 0 :
164177 raise ValueError ("No single machine in common with all of the results" )
165178
166- output_dir_path = Path (output_dir )
167- if not output_dir_path .exists ():
168- output_dir_path .mkdir ()
169-
170179 match comparison_type :
171180 case "1:n" :
172181 total = (len (parsed_commits ) - 1 ) * len (machines )
@@ -180,14 +189,61 @@ def _main(commits: Sequence[str], output_dir: PathLike, comparison_type: str):
180189 runners = mod_runners .get_runners_by_nickname ()
181190
182191 counter = [0 , total ]
183- with (output_dir_path / "README.md" ).open ("w" , encoding = "utf-8" ) as fd :
192+ with (output_dir / "README.md" ).open ("w" , encoding = "utf-8" ) as fd :
184193 for machine in machines :
185194 fd .write (f"# { runners [machine ].display_name } \n \n " )
186- func (fd , parsed_commits , machine , output_dir_path , counter )
195+ func (fd , parsed_commits , machine , output_dir , counter )
187196 fd .write ("\n " )
188197 rich .print ()
189198
190199
200+ def _main_with_files (commits : Sequence [str ], output_dir : Path , comparison_type : str ):
201+ parsed_results = []
202+ for commit in commits :
203+ if "," in commit :
204+ commit_path , name = commit .split ("," , 1 )
205+ commit_path = Path (commit_path )
206+ else :
207+ commit_path = Path (commit )
208+ name = commit_path .stem
209+ parsed_results .append (
210+ (name , [], name , [mod_result .Result .from_arbitrary_filename (commit_path )])
211+ )
212+
213+ match comparison_type :
214+ case "1:n" :
215+ total = len (commits ) - 1
216+ func = do_one_to_many
217+ case "n:n" :
218+ total = (len (commits ) ** 2 ) - len (commits )
219+ func = do_many_to_many
220+ case _:
221+ raise ValueError (f"Unknown comparison type { comparison_type } " )
222+
223+ counter = [0 , total ]
224+ with (output_dir / "README.md" ).open ("w" , encoding = "utf-8" ) as fd :
225+ fd .write ("# Comparisons\n \n " )
226+ func (fd , parsed_results , None , output_dir , counter )
227+ fd .write ("\n " )
228+ rich .print ()
229+
230+
231+ def _main (commits : Sequence [str ], output_dir : PathLike , comparison_type : str ):
232+ if len (commits ) < 2 :
233+ raise ValueError ("Must provide at least 2 commits" )
234+
235+ output_dir_path = Path (output_dir )
236+ if not output_dir_path .exists ():
237+ output_dir_path .mkdir ()
238+
239+ if all (commit .endswith (".json" ) for commit in commits ):
240+ _main_with_files (commits , output_dir_path , comparison_type )
241+ elif any (commit .endswith (".json" ) for commit in commits ):
242+ raise ValueError ("All commits must be either hashes or JSON files" )
243+ else :
244+ _main_with_hashes (commits , output_dir_path , comparison_type )
245+
246+
191247def main ():
192248 parser = argparse .ArgumentParser (
193249 description = """
@@ -204,7 +260,8 @@ def main():
204260 "commit" ,
205261 nargs = "+" ,
206262 help = """
207- Commits to compare. Must be a git commit hash prefix. May optionally
263+ Commits or files to compare. If ends with ".json", it is a path to a
264+ JSON file, otherwise, it is a git commit hash prefix. May optionally
208265 have a friendly name after a comma, e.g. c0ffee,main. If ends with
209266 a "T", use the Tier 2 run for that commit. If ends with a "J", use
210267 the JIT run for that commit. If ends with a "N", use the NOGIL run
0 commit comments