9393# Label produced by `valgrind_version` for CodSpeed's custom build.
9494CODSPEED_VERSION = "valgrind.codspeed"
9595
96+ # Default walltime sampling settings applied to every benchmark.
97+ #
98+ # Valgrind runs are slow (a single execution can take seconds), so with the
99+ # runner's default `max-time` of 3s the slowest benchmarks only complete a
100+ # single round. A one-sample estimate is dominated by run-to-run noise, which
101+ # is what makes these benchmarks unstable.
102+ #
103+ # We therefore ask the harness for a minimum number of measured rounds so
104+ # CodSpeed always has several samples to pick the representative time from,
105+ # plus one warmup round to discard cold-start effects (process spawn, page
106+ # faults, disk cache). `max-time` bounds the total wall time so fast
107+ # benchmarks don't over-run; when it is reached before `min-rounds`, the
108+ # harness stops early (max-time takes priority), keeping the workflow bounded.
109+ #
110+ # These are exposed as CLI flags so the CI workflow (and local runs) can tune
111+ # the stability/duration trade-off without editing this script.
112+ DEFAULT_WARMUP_TIME = "1s"
113+ DEFAULT_MIN_ROUNDS = 5
114+ DEFAULT_MAX_TIME = "20s"
115+
96116
97117def valgrind_version (valgrind_path : str ) -> str :
98118 """Return the normalized version label used in benchmark ids.
@@ -115,7 +135,12 @@ def valgrind_version(valgrind_path: str) -> str:
115135 return version
116136
117137
118- def build_config (valgrind_paths : list ) -> dict :
138+ def build_config (
139+ valgrind_paths : list ,
140+ warmup_time : str = DEFAULT_WARMUP_TIME ,
141+ min_rounds : int = DEFAULT_MIN_ROUNDS ,
142+ max_time : str = DEFAULT_MAX_TIME ,
143+ ) -> dict :
119144 """Build the codspeed.yml document for all valgrind builds and commands."""
120145 benchmarks = []
121146 for valgrind_path in valgrind_paths :
@@ -132,7 +157,18 @@ def build_config(valgrind_paths: list) -> dict:
132157 )
133158 benchmarks .append ({"name" : name , "exec" : exec_cmd })
134159
160+ # Root-level walltime options apply to every benchmark so all runs share the
161+ # same sampling policy. `min-rounds` guarantees several samples for a stable
162+ # estimate; `warmup-time` discards cold-start effects; `max-time` caps the
163+ # total per-benchmark wall time so the workflow stays bounded.
164+ walltime_options = {
165+ "warmup-time" : warmup_time ,
166+ "min-rounds" : min_rounds ,
167+ "max-time" : max_time ,
168+ }
169+
135170 return {
171+ "options" : {"walltime" : walltime_options },
136172 "benchmarks" : benchmarks ,
137173 }
138174
@@ -157,17 +193,46 @@ def main():
157193 default = "codspeed.yml" ,
158194 help = "Path to write the generated config (default: codspeed.yml)" ,
159195 )
196+ parser .add_argument (
197+ "--warmup-time" ,
198+ type = str ,
199+ default = DEFAULT_WARMUP_TIME ,
200+ help = "Walltime warmup duration applied to every benchmark, discarded "
201+ f"before measurement (default: { DEFAULT_WARMUP_TIME } ). Set to '0s' to disable." ,
202+ )
203+ parser .add_argument (
204+ "--min-rounds" ,
205+ type = int ,
206+ default = DEFAULT_MIN_ROUNDS ,
207+ help = "Minimum number of measured rounds per benchmark; more rounds give "
208+ f"a more stable estimate (default: { DEFAULT_MIN_ROUNDS } )." ,
209+ )
210+ parser .add_argument (
211+ "--max-time" ,
212+ type = str ,
213+ default = DEFAULT_MAX_TIME ,
214+ help = "Maximum total wall time per benchmark (includes warmup). Bounds the "
215+ "workflow duration; when reached before --min-rounds it takes priority "
216+ f"(default: { DEFAULT_MAX_TIME } )." ,
217+ )
160218 args = parser .parse_args ()
161219
162- config = build_config (args .valgrinds )
220+ config = build_config (
221+ args .valgrinds ,
222+ warmup_time = args .warmup_time ,
223+ min_rounds = args .min_rounds ,
224+ max_time = args .max_time ,
225+ )
163226
164227 with open (args .output , "w" ) as f :
165228 json .dump (config , f , indent = 2 )
166229 f .write ("\n " )
167230
168231 print (
169232 f"Wrote { args .output } with { len (config ['benchmarks' ])} benchmarks "
170- f"({ len (args .valgrinds )} valgrind builds x { len (COMMANDS )} commands x { len (CONFIGS )} configs)" ,
233+ f"({ len (args .valgrinds )} valgrind builds x { len (COMMANDS )} commands x { len (CONFIGS )} configs); "
234+ f"walltime options: warmup-time={ args .warmup_time } , min-rounds={ args .min_rounds } , "
235+ f"max-time={ args .max_time } " ,
171236 file = sys .stderr ,
172237 )
173238
0 commit comments