6
6
import datetime
7
7
import io
8
8
import multiprocessing
9
+ import os
9
10
from pathlib import Path
10
11
import sys
11
12
from typing import Iterable , Optional , TextIO
16
17
from bench_runner import plot
17
18
from bench_runner .result import (
18
19
load_all_results ,
19
- BenchmarkComparison ,
20
20
Comparison ,
21
21
Result ,
22
22
)
@@ -43,100 +43,118 @@ def recurse(entry: tuple, d: dict):
43
43
return d
44
44
45
45
46
- def write_markdown_results (filename : Path , compare : BenchmarkComparison ) -> None :
47
- if filename .exists ():
48
- filename .unlink ()
49
- compare = compare .copy ()
50
-
51
- contents = compare .contents
52
- if contents is None :
53
- return
54
-
55
- entries = [
56
- ("fork" , unquote (compare .head .fork )),
57
- ("ref" , compare .head .ref ),
58
- ("machine" , f"{ compare .head .system } -{ compare .head .machine } " ),
59
- ("commit hash" , compare .head .cpython_hash ),
60
- ("commit date" , compare .head .commit_date ),
61
- ("overall geometric mean" , compare .geometric_mean ),
62
- ("HPT reliability" , compare .hpt_reliability ),
63
- ("HPT 99th percentile" , compare .hpt_percentile (99 )),
64
- ]
46
+ class ResultWriter :
47
+ @staticmethod
48
+ def need_to_generate (compare : Comparison ) -> bool :
49
+ return False
50
+
51
+ @staticmethod
52
+ def generate (filename : Path , compare ) -> None :
53
+ pass
54
+
55
+
56
+ class MarkdownResultWriter (ResultWriter ):
57
+ @staticmethod
58
+ def need_to_generate (compare : Comparison ) -> bool :
59
+ return compare .contents is not None
60
+
61
+ @staticmethod
62
+ def generate (filename : Path , compare ) -> None :
63
+ if filename .exists ():
64
+ filename .unlink ()
65
+ compare = compare .copy ()
66
+
67
+ entries = [
68
+ ("fork" , unquote (compare .head .fork )),
69
+ ("ref" , compare .head .ref ),
70
+ ("machine" , f"{ compare .head .system } -{ compare .head .machine } " ),
71
+ ("commit hash" , compare .head .cpython_hash ),
72
+ ("commit date" , compare .head .commit_date ),
73
+ ("overall geometric mean" , compare .geometric_mean ),
74
+ ("HPT reliability" , compare .hpt_reliability ),
75
+ ("HPT 99th percentile" , compare .hpt_percentile (99 )),
76
+ ]
77
+
78
+ if compare .memory_change is not None :
79
+ entries .append (("Memory change" , compare .memory_change ))
80
+
81
+ with open (filename , "w" ) as fd :
82
+ fd .write (f"# Results vs. { compare .base } \n \n " )
83
+ for key , val in entries :
84
+ fd .write (f"- { key } : { val } \n " )
85
+ fd .write ("\n " )
86
+ fd .write (compare .contents or "" )
87
+
88
+
89
+ class TimePlotResultWriter (ResultWriter ):
90
+ @staticmethod
91
+ def need_to_generate (compare : Comparison ) -> bool :
92
+ return True
93
+
94
+ @staticmethod
95
+ def generate (filename : Path , compare ) -> None :
96
+ plot .plot_diff (
97
+ compare .get_timing_diff (),
98
+ filename ,
99
+ (
100
+ "Timings of "
101
+ f"{ unquote (compare .head .fork )} -{ compare .head .ref } -"
102
+ f"{ compare .head .cpython_hash } "
103
+ f" vs. { compare .ref .version } "
104
+ ),
105
+ ("slower" , "faster" ),
106
+ )
65
107
66
- if compare .memory_change is not None :
67
- entries .append (("Memory change" , compare .memory_change ))
68
-
69
- with open (filename , "w" ) as fd :
70
- fd .write (f"# Results vs. { compare .base } \n \n " )
71
- for key , val in entries :
72
- fd .write (f"- { key } : { val } \n " )
73
- fd .write ("\n " )
74
- fd .write (contents )
75
-
76
-
77
- def write_time_plot_results (filename : Path , compare : BenchmarkComparison ) -> None :
78
- plot .plot_diff (
79
- compare .get_timing_diff (),
80
- filename ,
81
- (
82
- "Timings of "
83
- f"{ unquote (compare .head .fork )} -{ compare .head .ref } -"
84
- f"{ compare .head .cpython_hash } "
85
- f" vs. { compare .ref .version } "
86
- ),
87
- ("slower" , "faster" ),
88
- )
89
108
109
+ class MemoryPlotResultWriter (ResultWriter ):
110
+ @staticmethod
111
+ def need_to_generate (compare : Comparison ) -> bool :
112
+ return compare .head .system != "windows" and compare .base == "base"
90
113
91
- def write_memory_plot_results (filename : Path , compare : BenchmarkComparison ) -> None :
92
- if compare .head .system == "windows" :
93
- # Windows doesn't have memory data
94
- return
95
-
96
- if compare .base != "base" :
97
- # Only generate the graphs for base comparisons
98
- return
99
-
100
- plot .plot_diff (
101
- compare .get_memory_diff (),
102
- filename ,
103
- (
104
- "Memory usage of "
105
- f"{ unquote (compare .head .fork )} -{ compare .head .ref } -"
106
- f"{ compare .head .cpython_hash } "
107
- f" vs. { compare .ref .version } "
108
- ),
109
- ("less" , "more" ),
110
- )
114
+ @staticmethod
115
+ def generate (filename : Path , compare ) -> None :
116
+ plot .plot_diff (
117
+ compare .get_memory_diff (),
118
+ filename ,
119
+ (
120
+ "Memory usage of "
121
+ f"{ unquote (compare .head .fork )} -{ compare .head .ref } -"
122
+ f"{ compare .head .cpython_hash } "
123
+ f" vs. { compare .ref .version } "
124
+ ),
125
+ ("less" , "more" ),
126
+ )
111
127
112
128
113
- def write_pystats_diff ( filename : Path , compare : Comparison ) -> None :
114
- if filename . exists ():
115
- filename . unlink ()
116
- compare = compare .copy ()
129
+ class PystatsDiffResultWriter ( ResultWriter ) :
130
+ @ staticmethod
131
+ def need_to_generate ( compare : Comparison ) -> bool :
132
+ return compare .contents is not None
117
133
118
- contents = compare .contents
119
- if contents is None :
120
- return
134
+ @staticmethod
135
+ def generate (filename : Path , compare ) -> None :
136
+ if filename .exists ():
137
+ filename .unlink ()
138
+ compare = compare .copy ()
121
139
122
- with open (filename , "w" ) as fd :
123
- fd .write (contents )
140
+ with open (filename , "w" ) as fd :
141
+ fd .write (compare . contents or "" )
124
142
125
143
126
144
RESULT_TYPES = {
127
145
"raw results" : {
128
- ".md" : write_markdown_results ,
129
- ".png" : write_time_plot_results ,
130
- "-mem.png" : write_memory_plot_results ,
146
+ ".md" : MarkdownResultWriter ,
147
+ ".png" : TimePlotResultWriter ,
148
+ "-mem.png" : MemoryPlotResultWriter ,
131
149
},
132
- "pystats raw" : {".md" : write_pystats_diff },
150
+ "pystats raw" : {".md" : PystatsDiffResultWriter },
133
151
None : {},
134
152
}
135
153
136
154
137
- def _worker (args ):
138
- func , output_filename , compare = args
139
- func (output_filename , compare )
155
+ def _worker (args ) -> None :
156
+ writer , output_filename , compare = args
157
+ writer . generate (output_filename , compare )
140
158
141
159
142
160
def save_generated_results (results : Iterable [Result ], force : bool = False ) -> None :
@@ -147,19 +165,31 @@ def save_generated_results(results: Iterable[Result], force: bool = False) -> No
147
165
regeneration, pass ``force=True``.
148
166
"""
149
167
work = []
168
+ directories_affected = set ()
150
169
for result in results :
151
170
for compare in result .bases .values ():
152
171
if compare .filename is not None :
153
- for suffix , func in RESULT_TYPES [result .result_info [0 ]].items ():
172
+ for suffix , writer in RESULT_TYPES [result .result_info [0 ]].items ():
154
173
filename = compare .filename .parent / (
155
174
compare .filename .stem + suffix
156
175
)
157
- if not filename .exists () or force : # or suffix == ".png":
158
- work .append ((func , filename , compare ))
159
-
160
- pool = multiprocessing .Pool ()
161
- for i , _ in enumerate (pool .imap_unordered (_worker , work )):
162
- print (f"{ i :04d} /{ len (work ):04d} " , end = "\r " )
176
+ if writer .need_to_generate (compare ) and (
177
+ not filename .exists () or force
178
+ ):
179
+ work .append ((writer , filename , compare ))
180
+ directories_affected .add (filename .parent )
181
+
182
+ with multiprocessing .Pool () as pool :
183
+ for i , _ in enumerate (pool .imap_unordered (_worker , work )):
184
+ print (f"{ i + 1 :04d} /{ len (work ):04d} " , end = "\r " )
185
+ print ()
186
+
187
+ github_repo = os .environ .get ("GITHUB_REPOSITORY" , "UNKNOWN" )
188
+ for directory in directories_affected :
189
+ print (
190
+ "::notice ::New results at "
191
+ f"https://github.com/{ github_repo } _public/tree/main/{ directory } "
192
+ )
163
193
164
194
165
195
def output_results_index (
0 commit comments