holobench 1.41.0__py3-none-any.whl → 1.43.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bencher/__init__.py +20 -2
- bencher/bench_cfg.py +262 -54
- bencher/bench_report.py +2 -2
- bencher/bench_runner.py +96 -10
- bencher/bencher.py +421 -89
- bencher/class_enum.py +70 -7
- bencher/example/example_dataframe.py +2 -2
- bencher/example/example_levels.py +17 -173
- bencher/example/example_pareto.py +107 -31
- bencher/example/example_rerun2.py +1 -1
- bencher/example/example_simple_bool.py +2 -2
- bencher/example/example_simple_float2d.py +6 -1
- bencher/example/example_video.py +2 -0
- bencher/example/experimental/example_hvplot_explorer.py +2 -2
- bencher/example/inputs_0D/example_0_in_1_out.py +25 -15
- bencher/example/inputs_0D/example_0_in_2_out.py +12 -3
- bencher/example/inputs_0_float/example_0_cat_in_2_out.py +88 -0
- bencher/example/inputs_0_float/example_1_cat_in_2_out.py +98 -0
- bencher/example/inputs_0_float/example_2_cat_in_2_out.py +107 -0
- bencher/example/inputs_0_float/example_3_cat_in_2_out.py +111 -0
- bencher/example/inputs_1D/example1d_common.py +48 -12
- bencher/example/inputs_1D/example_0_float_1_cat.py +33 -0
- bencher/example/inputs_1D/example_1_cat_in_2_out_repeats.py +68 -0
- bencher/example/inputs_1D/example_1_float_2_cat_repeats.py +3 -0
- bencher/example/inputs_1D/example_1_int_in_1_out.py +98 -0
- bencher/example/inputs_1D/example_1_int_in_2_out.py +101 -0
- bencher/example/inputs_1D/example_1_int_in_2_out_repeats.py +99 -0
- bencher/example/inputs_1_float/example_1_float_0_cat_in_2_out.py +117 -0
- bencher/example/inputs_1_float/example_1_float_1_cat_in_2_out.py +124 -0
- bencher/example/inputs_1_float/example_1_float_2_cat_in_2_out.py +132 -0
- bencher/example/inputs_1_float/example_1_float_3_cat_in_2_out.py +140 -0
- bencher/example/inputs_2D/example_2_cat_in_4_out_repeats.py +104 -0
- bencher/example/inputs_2_float/example_2_float_0_cat_in_2_out.py +98 -0
- bencher/example/inputs_2_float/example_2_float_1_cat_in_2_out.py +112 -0
- bencher/example/inputs_2_float/example_2_float_2_cat_in_2_out.py +122 -0
- bencher/example/inputs_2_float/example_2_float_3_cat_in_2_out.py +138 -0
- bencher/example/inputs_3_float/example_3_float_0_cat_in_2_out.py +111 -0
- bencher/example/inputs_3_float/example_3_float_1_cat_in_2_out.py +117 -0
- bencher/example/inputs_3_float/example_3_float_2_cat_in_2_out.py +124 -0
- bencher/example/inputs_3_float/example_3_float_3_cat_in_2_out.py +129 -0
- bencher/example/meta/generate_examples.py +118 -7
- bencher/example/meta/generate_meta.py +88 -40
- bencher/job.py +174 -9
- bencher/plotting/plot_filter.py +52 -17
- bencher/results/bench_result.py +117 -25
- bencher/results/bench_result_base.py +117 -8
- bencher/results/dataset_result.py +6 -200
- bencher/results/explorer_result.py +23 -0
- bencher/results/{hvplot_result.py → histogram_result.py} +3 -18
- bencher/results/holoview_results/__init__.py +0 -0
- bencher/results/holoview_results/bar_result.py +79 -0
- bencher/results/holoview_results/curve_result.py +110 -0
- bencher/results/holoview_results/distribution_result/__init__.py +0 -0
- bencher/results/holoview_results/distribution_result/box_whisker_result.py +73 -0
- bencher/results/holoview_results/distribution_result/distribution_result.py +109 -0
- bencher/results/holoview_results/distribution_result/scatter_jitter_result.py +92 -0
- bencher/results/holoview_results/distribution_result/violin_result.py +70 -0
- bencher/results/holoview_results/heatmap_result.py +319 -0
- bencher/results/holoview_results/holoview_result.py +346 -0
- bencher/results/holoview_results/line_result.py +240 -0
- bencher/results/holoview_results/scatter_result.py +107 -0
- bencher/results/holoview_results/surface_result.py +158 -0
- bencher/results/holoview_results/table_result.py +14 -0
- bencher/results/holoview_results/tabulator_result.py +20 -0
- bencher/results/optuna_result.py +30 -115
- bencher/results/video_controls.py +38 -0
- bencher/results/video_result.py +39 -36
- bencher/results/video_summary.py +2 -2
- bencher/results/{plotly_result.py → volume_result.py} +29 -8
- bencher/utils.py +175 -26
- bencher/variables/inputs.py +122 -15
- bencher/video_writer.py +2 -1
- bencher/worker_job.py +31 -3
- {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/METADATA +24 -24
- holobench-1.43.0.dist-info/RECORD +147 -0
- bencher/example/example_levels2.py +0 -37
- bencher/example/inputs_1D/example_1_in_1_out.py +0 -62
- bencher/example/inputs_1D/example_1_in_2_out.py +0 -63
- bencher/example/inputs_1D/example_1_in_2_out_repeats.py +0 -61
- bencher/results/holoview_result.py +0 -796
- bencher/results/panel_result.py +0 -41
- holobench-1.41.0.dist-info/RECORD +0 -114
- {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/WHEEL +0 -0
- {holobench-1.41.0.dist-info → holobench-1.43.0.dist-info}/licenses/LICENSE +0 -0
bencher/bencher.py
CHANGED
@@ -2,7 +2,8 @@ import logging
|
|
2
2
|
from datetime import datetime
|
3
3
|
from itertools import product, combinations
|
4
4
|
|
5
|
-
from
|
5
|
+
from param import Parameter
|
6
|
+
from typing import Callable, List, Optional, Tuple, Any
|
6
7
|
from copy import deepcopy
|
7
8
|
import numpy as np
|
8
9
|
import param
|
@@ -46,8 +47,22 @@ for handler in logging.root.handlers:
|
|
46
47
|
handler.setFormatter(formatter)
|
47
48
|
|
48
49
|
|
49
|
-
def set_xarray_multidim(
|
50
|
-
|
50
|
+
def set_xarray_multidim(
|
51
|
+
data_array: xr.DataArray, index_tuple: Tuple[int, ...], value: Any
|
52
|
+
) -> xr.DataArray:
|
53
|
+
"""Set a value in a multi-dimensional xarray at the specified index position.
|
54
|
+
|
55
|
+
This function sets a value in an N-dimensional xarray using explicit matching on the
|
56
|
+
tuple length since direct indexing with variable length index tuples is not supported.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
data_array (xr.DataArray): The data array to modify
|
60
|
+
index_tuple (Tuple[int, ...]): The index coordinates as a tuple
|
61
|
+
value (Any): The value to set at the specified position
|
62
|
+
|
63
|
+
Returns:
|
64
|
+
xr.DataArray: The modified data array
|
65
|
+
"""
|
51
66
|
match len(index_tuple):
|
52
67
|
case 1:
|
53
68
|
data_array[index_tuple[0]] = value
|
@@ -108,17 +123,52 @@ def set_xarray_multidim(data_array: xr.DataArray, index_tuple, value: float) ->
|
|
108
123
|
|
109
124
|
|
110
125
|
def kwargs_to_input_cfg(worker_input_cfg: ParametrizedSweep, **kwargs) -> ParametrizedSweep:
|
126
|
+
"""Create a configured instance of a ParametrizedSweep with the provided keyword arguments.
|
127
|
+
|
128
|
+
Args:
|
129
|
+
worker_input_cfg (ParametrizedSweep): The ParametrizedSweep class to instantiate
|
130
|
+
**kwargs: Keyword arguments to update the configuration with
|
131
|
+
|
132
|
+
Returns:
|
133
|
+
ParametrizedSweep: A configured instance of the worker_input_cfg class
|
134
|
+
"""
|
111
135
|
input_cfg = worker_input_cfg()
|
112
136
|
input_cfg.param.update(kwargs)
|
113
137
|
return input_cfg
|
114
138
|
|
115
139
|
|
116
|
-
def worker_cfg_wrapper(worker, worker_input_cfg: ParametrizedSweep, **kwargs) -> dict:
|
140
|
+
def worker_cfg_wrapper(worker: Callable, worker_input_cfg: ParametrizedSweep, **kwargs) -> dict:
|
141
|
+
"""Wrap a worker function to accept keyword arguments instead of a config object.
|
142
|
+
|
143
|
+
This wrapper creates an instance of the worker_input_cfg class, updates it with the
|
144
|
+
provided keyword arguments, and passes it to the worker function.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
worker (Callable): The worker function that expects a config object
|
148
|
+
worker_input_cfg (ParametrizedSweep): The class defining the configuration
|
149
|
+
**kwargs: Keyword arguments to update the configuration with
|
150
|
+
|
151
|
+
Returns:
|
152
|
+
dict: The result of calling the worker function with the configured input
|
153
|
+
"""
|
117
154
|
input_cfg = kwargs_to_input_cfg(worker_input_cfg, **kwargs)
|
118
155
|
return worker(input_cfg)
|
119
156
|
|
120
157
|
|
121
158
|
def worker_kwargs_wrapper(worker: Callable, bench_cfg: BenchCfg, **kwargs) -> dict:
|
159
|
+
"""Prepare keyword arguments and pass them to a worker function.
|
160
|
+
|
161
|
+
This wrapper helps filter out metadata parameters that should not be passed
|
162
|
+
to the worker function (like 'repeat', 'over_time', and 'time_event').
|
163
|
+
|
164
|
+
Args:
|
165
|
+
worker (Callable): The worker function to call
|
166
|
+
bench_cfg (BenchCfg): Benchmark configuration with parameters like pass_repeat
|
167
|
+
**kwargs: The keyword arguments to filter and pass to the worker
|
168
|
+
|
169
|
+
Returns:
|
170
|
+
dict: The result from the worker function
|
171
|
+
"""
|
122
172
|
function_input_deep = deepcopy(kwargs)
|
123
173
|
if not bench_cfg.pass_repeat:
|
124
174
|
function_input_deep.pop("repeat")
|
@@ -135,15 +185,32 @@ class Bench(BenchPlotServer):
|
|
135
185
|
bench_name: str = None,
|
136
186
|
worker: Callable | ParametrizedSweep = None,
|
137
187
|
worker_input_cfg: ParametrizedSweep = None,
|
138
|
-
run_cfg=None,
|
139
|
-
report=None,
|
188
|
+
run_cfg: BenchRunCfg = None,
|
189
|
+
report: BenchReport = None,
|
140
190
|
) -> None:
|
141
|
-
"""Create a new Bench object
|
191
|
+
"""Create a new Bench object for benchmarking a worker function with parametrized inputs.
|
192
|
+
|
193
|
+
This initializes a benchmarking environment that can execute and visualize the performance
|
194
|
+
of a worker function across different parameter combinations. The worker can be either a
|
195
|
+
callable function or a ParametrizedSweep instance with a __call__ method.
|
142
196
|
|
143
197
|
Args:
|
144
|
-
bench_name (str): The name of the benchmark and output folder for the figures
|
145
|
-
|
146
|
-
|
198
|
+
bench_name (str): The name of the benchmark and output folder for the figures.
|
199
|
+
Must be a string.
|
200
|
+
worker (Callable | ParametrizedSweep, optional): Either a function that accepts a
|
201
|
+
ParametrizedSweep instance, or a ParametrizedSweep instance with a __call__ method.
|
202
|
+
Defaults to None.
|
203
|
+
worker_input_cfg (ParametrizedSweep, optional): A class defining the parameters expected
|
204
|
+
by the worker function. Only needed if worker is a function rather than a
|
205
|
+
ParametrizedSweep instance. Defaults to None.
|
206
|
+
run_cfg (BenchRunCfg, optional): Configuration parameters for the benchmark run,
|
207
|
+
such as caching settings, execution mode, etc. Defaults to None.
|
208
|
+
report (BenchReport, optional): An existing report to append benchmark results to.
|
209
|
+
If None, a new report will be created. Defaults to None.
|
210
|
+
|
211
|
+
Raises:
|
212
|
+
AssertionError: If bench_name is not a string.
|
213
|
+
RuntimeError: If worker is a class type instead of an instance.
|
147
214
|
"""
|
148
215
|
assert isinstance(bench_name, str)
|
149
216
|
self.bench_name = bench_name
|
@@ -178,21 +245,42 @@ class Bench(BenchPlotServer):
|
|
178
245
|
self.plot = True
|
179
246
|
|
180
247
|
def add_plot_callback(self, callback: Callable[[BenchResult], pn.panel], **kwargs) -> None:
|
181
|
-
"""Add a plotting callback
|
248
|
+
"""Add a plotting callback to be called on benchmark results.
|
249
|
+
|
250
|
+
This method registers a plotting function that will be automatically called on any
|
251
|
+
BenchResult produced when running a sweep. You can pass additional arguments to
|
252
|
+
the plotting function using keyword arguments.
|
182
253
|
|
183
254
|
Args:
|
184
|
-
callback (Callable[[BenchResult], pn.panel]):
|
255
|
+
callback (Callable[[BenchResult], pn.panel]): A function that takes a BenchResult
|
256
|
+
and returns a panel object. For example: BenchResult.to_video_grid
|
257
|
+
**kwargs: Additional keyword arguments to pass to the callback function
|
258
|
+
|
259
|
+
Examples:
|
260
|
+
>>> bench.add_plot_callback(BenchResult.to_video_grid, width=800)
|
185
261
|
"""
|
186
262
|
self.plot_callbacks.append(partial(callback, **kwargs))
|
187
263
|
|
188
|
-
def set_worker(
|
189
|
-
|
264
|
+
def set_worker(
|
265
|
+
self, worker: Callable | ParametrizedSweep, worker_input_cfg: ParametrizedSweep = None
|
266
|
+
) -> None:
|
267
|
+
"""Set the benchmark worker function and its input configuration.
|
268
|
+
|
269
|
+
This method sets up the worker function to be benchmarked. The worker can be either a
|
270
|
+
callable function that takes a ParametrizedSweep instance or a ParametrizedSweep
|
271
|
+
instance with a __call__ method. In the latter case, worker_input_cfg is not needed.
|
190
272
|
|
191
273
|
Args:
|
192
|
-
worker (Callable):
|
193
|
-
|
194
|
-
|
274
|
+
worker (Callable | ParametrizedSweep): Either a function that will be benchmarked or a
|
275
|
+
ParametrizedSweep instance with a __call__ method. When a ParametrizedSweep is provided,
|
276
|
+
its __call__ method becomes the worker function.
|
277
|
+
worker_input_cfg (ParametrizedSweep, optional): The class defining the input parameters
|
278
|
+
for the worker function. Only needed if worker is a function rather than a
|
279
|
+
ParametrizedSweep instance. Defaults to None.
|
195
280
|
|
281
|
+
Raises:
|
282
|
+
RuntimeError: If worker is a class type instead of an instance.
|
283
|
+
"""
|
196
284
|
if isinstance(worker, ParametrizedSweep):
|
197
285
|
self.worker_class_instance = worker
|
198
286
|
# self.worker_class_type = type(worker)
|
@@ -210,7 +298,7 @@ class Bench(BenchPlotServer):
|
|
210
298
|
|
211
299
|
def sweep_sequential(
|
212
300
|
self,
|
213
|
-
title="",
|
301
|
+
title: str = "",
|
214
302
|
input_vars: List[ParametrizedSweep] = None,
|
215
303
|
result_vars: List[ParametrizedSweep] = None,
|
216
304
|
const_vars: List[ParametrizedSweep] = None,
|
@@ -218,9 +306,33 @@ class Bench(BenchPlotServer):
|
|
218
306
|
run_cfg: BenchRunCfg = None,
|
219
307
|
group_size: int = 1,
|
220
308
|
iterations: int = 1,
|
221
|
-
relationship_cb=None,
|
222
|
-
plot_callbacks: List | bool = None,
|
309
|
+
relationship_cb: Callable = None,
|
310
|
+
plot_callbacks: List[Callable] | bool = None,
|
223
311
|
) -> List[BenchResult]:
|
312
|
+
"""Run a sequence of benchmarks by sweeping through groups of input variables.
|
313
|
+
|
314
|
+
This method performs sweeps on combinations of input variables, potentially
|
315
|
+
using the optimal value from each sweep as constants for the next iteration.
|
316
|
+
|
317
|
+
Args:
|
318
|
+
title (str, optional): Base title for all the benchmark sweeps. Defaults to "".
|
319
|
+
input_vars (List[ParametrizedSweep], optional): Input variables to sweep through.
|
320
|
+
If None, defaults to all input variables from the worker class instance.
|
321
|
+
result_vars (List[ParametrizedSweep], optional): Result variables to collect. Defaults to None.
|
322
|
+
const_vars (List[ParametrizedSweep], optional): Variables to keep constant. Defaults to None.
|
323
|
+
optimise_var (ParametrizedSweep, optional): Variable to optimize on each sweep iteration.
|
324
|
+
The optimal value will be used as constant input for subsequent sweeps. Defaults to None.
|
325
|
+
run_cfg (BenchRunCfg, optional): Run configuration. Defaults to None.
|
326
|
+
group_size (int, optional): Number of input variables to sweep together in each run. Defaults to 1.
|
327
|
+
iterations (int, optional): Number of optimization iterations to perform. Defaults to 1.
|
328
|
+
relationship_cb (Callable, optional): Function to determine how to group variables for sweeping.
|
329
|
+
Defaults to itertools.combinations if None.
|
330
|
+
plot_callbacks (List[Callable] | bool, optional): Callbacks for plotting or bool to enable/disable.
|
331
|
+
Defaults to None.
|
332
|
+
|
333
|
+
Returns:
|
334
|
+
List[BenchResult]: A list of results from all the sweep runs
|
335
|
+
"""
|
224
336
|
if relationship_cb is None:
|
225
337
|
relationship_cb = combinations
|
226
338
|
if input_vars is None:
|
@@ -257,28 +369,43 @@ class Bench(BenchPlotServer):
|
|
257
369
|
pass_repeat: bool = False,
|
258
370
|
tag: str = "",
|
259
371
|
run_cfg: BenchRunCfg = None,
|
260
|
-
plot_callbacks: List | bool = None,
|
372
|
+
plot_callbacks: List[Callable] | bool = None,
|
261
373
|
) -> BenchResult:
|
262
|
-
"""The all
|
374
|
+
"""The all-in-one function for benchmarking and results plotting.
|
375
|
+
|
376
|
+
This is the main function for performing benchmark sweeps. It handles all the setup,
|
377
|
+
execution, and visualization of benchmarks based on the input parameters.
|
263
378
|
|
264
379
|
Args:
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
380
|
+
title (str, optional): The title of the benchmark. If None, a title will be
|
381
|
+
generated based on the input variables. Defaults to None.
|
382
|
+
input_vars (List[ParametrizedSweep], optional): Variables to sweep through in the benchmark.
|
383
|
+
If None and worker_class_instance exists, uses input variables from it. Defaults to None.
|
384
|
+
result_vars (List[ParametrizedSweep], optional): Variables to collect results for.
|
385
|
+
If None and worker_class_instance exists, uses result variables from it. Defaults to None.
|
386
|
+
const_vars (List[ParametrizedSweep], optional): Variables to keep constant with specified values.
|
387
|
+
If None and worker_class_instance exists, uses default input values. Defaults to None.
|
388
|
+
time_src (datetime, optional): The timestamp for the benchmark. Used for time-series benchmarks.
|
389
|
+
Defaults to None, which will use the current time.
|
390
|
+
description (str, optional): A description displayed before the benchmark plots. Defaults to None.
|
391
|
+
post_description (str, optional): A description displayed after the benchmark plots.
|
392
|
+
Defaults to a generic message recommending to set a custom description.
|
393
|
+
pass_repeat (bool, optional): If True, passes the 'repeat' parameter to the worker function.
|
394
|
+
Defaults to False.
|
395
|
+
tag (str, optional): Tag to group different benchmarks together. Defaults to "".
|
396
|
+
run_cfg (BenchRunCfg, optional): Configuration for how the benchmarks are run.
|
397
|
+
If None, uses the instance's run_cfg or creates a default one. Defaults to None.
|
398
|
+
plot_callbacks (List[Callable] | bool, optional): Callbacks for plotting results.
|
399
|
+
If True, uses default plotting. If False, disables plotting.
|
400
|
+
If a list, uses the provided callbacks. Defaults to None.
|
279
401
|
|
280
402
|
Returns:
|
281
|
-
BenchResult:
|
403
|
+
BenchResult: An object containing all the benchmark data and results
|
404
|
+
|
405
|
+
Raises:
|
406
|
+
RuntimeError: If an unsupported input variable type is provided
|
407
|
+
TypeError: If variable parameters are not of the correct type
|
408
|
+
FileNotFoundError: If only_plot=True and no cached results exist
|
282
409
|
"""
|
283
410
|
|
284
411
|
input_vars_in = deepcopy(input_vars)
|
@@ -301,7 +428,7 @@ class Bench(BenchPlotServer):
|
|
301
428
|
"No results variables passed, using all result variables in bench class:"
|
302
429
|
)
|
303
430
|
if self.result_vars is None:
|
304
|
-
result_vars_in = self.
|
431
|
+
result_vars_in = self.get_result_vars(as_str=False)
|
305
432
|
else:
|
306
433
|
result_vars_in = deepcopy(self.result_vars)
|
307
434
|
|
@@ -427,8 +554,26 @@ class Bench(BenchPlotServer):
|
|
427
554
|
return self.run_sweep(bench_cfg, run_cfg, time_src)
|
428
555
|
|
429
556
|
def run_sweep(
|
430
|
-
self, bench_cfg: BenchCfg, run_cfg: BenchRunCfg, time_src: datetime
|
557
|
+
self, bench_cfg: BenchCfg, run_cfg: BenchRunCfg, time_src: datetime = None
|
431
558
|
) -> BenchResult:
|
559
|
+
"""Execute a benchmark sweep based on the provided configuration.
|
560
|
+
|
561
|
+
This method handles the caching, execution, and post-processing of a benchmark sweep
|
562
|
+
according to the provided configurations. It's typically called by `plot_sweep` rather
|
563
|
+
than directly by users.
|
564
|
+
|
565
|
+
Args:
|
566
|
+
bench_cfg (BenchCfg): Configuration defining inputs, results, and other benchmark parameters
|
567
|
+
run_cfg (BenchRunCfg): Configuration for how the benchmark should be executed
|
568
|
+
time_src (datetime, optional): The timestamp for the benchmark. Used for time-series benchmarks.
|
569
|
+
Defaults to None, which will use the current time.
|
570
|
+
|
571
|
+
Returns:
|
572
|
+
BenchResult: An object containing all benchmark data, results, and visualization
|
573
|
+
|
574
|
+
Raises:
|
575
|
+
FileNotFoundError: If only_plot=True and no cached results exist
|
576
|
+
"""
|
432
577
|
print("tag", bench_cfg.tag)
|
433
578
|
|
434
579
|
bench_cfg.param.update(run_cfg.param.values())
|
@@ -495,14 +640,27 @@ class Bench(BenchPlotServer):
|
|
495
640
|
var_type: str,
|
496
641
|
run_cfg: Optional[BenchRunCfg],
|
497
642
|
) -> param.Parameter:
|
498
|
-
"""
|
643
|
+
"""Convert various input formats to param.Parameter objects.
|
644
|
+
|
645
|
+
This method handles different ways of specifying variables in benchmark sweeps,
|
646
|
+
including direct param.Parameter objects, string names of parameters, or dictionaries
|
647
|
+
with parameter configuration details. It ensures all inputs are properly converted
|
648
|
+
to param.Parameter objects with the correct configuration.
|
499
649
|
|
500
650
|
Args:
|
501
|
-
variable (param.Parameter):
|
502
|
-
|
651
|
+
variable (param.Parameter | str | dict | tuple): The variable to convert, can be:
|
652
|
+
- param.Parameter: Already a parameter object
|
653
|
+
- str: Name of a parameter in the worker_class_instance
|
654
|
+
- dict: Configuration with 'name' and optional 'values', 'samples', 'max_level'
|
655
|
+
- tuple: Tuple that can be converted to a parameter
|
656
|
+
var_type (str): Type of variable ('input', 'result', or 'const') for error messages
|
657
|
+
run_cfg (Optional[BenchRunCfg]): Run configuration for level settings
|
658
|
+
|
659
|
+
Returns:
|
660
|
+
param.Parameter: The converted parameter object
|
503
661
|
|
504
662
|
Raises:
|
505
|
-
TypeError: the
|
663
|
+
TypeError: If the variable cannot be converted to a param.Parameter
|
506
664
|
"""
|
507
665
|
if isinstance(variable, str):
|
508
666
|
variable = self.worker_class_instance.param.objects(instance=False)[variable]
|
@@ -523,7 +681,17 @@ class Bench(BenchPlotServer):
|
|
523
681
|
)
|
524
682
|
return variable
|
525
683
|
|
526
|
-
def cache_results(self, bench_res: BenchResult, bench_cfg_hash:
|
684
|
+
def cache_results(self, bench_res: BenchResult, bench_cfg_hash: str) -> None:
|
685
|
+
"""Cache benchmark results for future retrieval.
|
686
|
+
|
687
|
+
This method stores benchmark results in the disk cache using the benchmark
|
688
|
+
configuration hash as the key. It temporarily removes non-pickleable objects
|
689
|
+
from the benchmark result before caching.
|
690
|
+
|
691
|
+
Args:
|
692
|
+
bench_res (BenchResult): The benchmark result to cache
|
693
|
+
bench_cfg_hash (str): The hash value to use as the cache key
|
694
|
+
"""
|
527
695
|
with Cache("cachedir/benchmark_inputs", size_limit=self.cache_size) as c:
|
528
696
|
logging.info(f"saving results with key: {bench_cfg_hash}")
|
529
697
|
self.bench_cfg_hashes.append(bench_cfg_hash)
|
@@ -539,33 +707,47 @@ class Bench(BenchPlotServer):
|
|
539
707
|
logging.info(f"saving benchmark: {self.bench_name}")
|
540
708
|
c[self.bench_name] = self.bench_cfg_hashes
|
541
709
|
|
542
|
-
# def show(self, run_cfg: BenchRunCfg = None, pane=None) -> None:
|
543
|
-
# """
|
544
|
-
|
710
|
+
# def show(self, run_cfg: BenchRunCfg = None, pane: pn.panel = None) -> None:
|
711
|
+
# """Launch a web server with plots of the benchmark results.
|
712
|
+
#
|
713
|
+
# This method starts a Panel web server to display the benchmark results interactively.
|
714
|
+
# It is a blocking call that runs until the server is terminated.
|
715
|
+
#
|
545
716
|
# Args:
|
546
|
-
# run_cfg (BenchRunCfg, optional):
|
547
|
-
|
717
|
+
# run_cfg (BenchRunCfg, optional): Configuration options for the web server,
|
718
|
+
# such as the port number. If None, uses the instance's last_run_cfg
|
719
|
+
# or creates a default one. Defaults to None.
|
720
|
+
# pane (pn.panel, optional): A custom panel to display instead of the default
|
721
|
+
# benchmark visualization. Defaults to None.
|
722
|
+
#
|
723
|
+
# Returns:
|
724
|
+
# None
|
548
725
|
# """
|
549
726
|
# if run_cfg is None:
|
550
727
|
# if self.last_run_cfg is not None:
|
551
728
|
# run_cfg = self.last_run_cfg
|
552
729
|
# else:
|
553
730
|
# run_cfg = BenchRunCfg()
|
554
|
-
|
731
|
+
#
|
555
732
|
# return BenchPlotServer().plot_server(self.bench_name, run_cfg, pane)
|
556
733
|
|
557
734
|
def load_history_cache(
|
558
|
-
self, dataset: xr.Dataset, bench_cfg_hash:
|
735
|
+
self, dataset: xr.Dataset, bench_cfg_hash: str, clear_history: bool
|
559
736
|
) -> xr.Dataset:
|
560
|
-
"""Load historical data from a cache if over_time
|
737
|
+
"""Load historical data from a cache if over_time is enabled.
|
738
|
+
|
739
|
+
This method is used to retrieve and concatenate historical benchmark data from the cache
|
740
|
+
when tracking performance over time. If clear_history is True, it will clear any existing
|
741
|
+
historical data instead of loading it.
|
561
742
|
|
562
743
|
Args:
|
563
|
-
|
564
|
-
bench_cfg_hash (
|
565
|
-
clear_history (bool):
|
744
|
+
dataset (xr.Dataset): Freshly calculated benchmark data for the current run
|
745
|
+
bench_cfg_hash (str): Hash of the input variables used to identify cached data
|
746
|
+
clear_history (bool): If True, clears historical data instead of loading it
|
566
747
|
|
567
748
|
Returns:
|
568
|
-
xr.Dataset: historical
|
749
|
+
xr.Dataset: Combined dataset with both historical and current benchmark data,
|
750
|
+
or just the current data if no history exists or history is cleared
|
569
751
|
"""
|
570
752
|
with Cache("cachedir/history", size_limit=self.cache_size) as c:
|
571
753
|
if clear_history:
|
@@ -585,17 +767,23 @@ class Bench(BenchPlotServer):
|
|
585
767
|
|
586
768
|
def setup_dataset(
|
587
769
|
self, bench_cfg: BenchCfg, time_src: datetime | str
|
588
|
-
) -> tuple[BenchResult, List, List]:
|
589
|
-
"""
|
770
|
+
) -> tuple[BenchResult, List[tuple], List[str]]:
|
771
|
+
"""Initialize an n-dimensional xarray dataset from benchmark configuration parameters.
|
772
|
+
|
773
|
+
This function creates the data structures needed to store benchmark results based on
|
774
|
+
the provided configuration. It sets up the xarray dimensions, coordinates, and variables
|
775
|
+
based on input variables and result variables.
|
590
776
|
|
591
777
|
Args:
|
592
|
-
bench_cfg (BenchCfg):
|
593
|
-
time_src (datetime | str):
|
778
|
+
bench_cfg (BenchCfg): Configuration defining the benchmark parameters, inputs, and results
|
779
|
+
time_src (datetime | str): Timestamp or event name for the benchmark run
|
594
780
|
|
595
781
|
Returns:
|
596
|
-
tuple[BenchResult, List, List]:
|
782
|
+
tuple[BenchResult, List[tuple], List[str]]:
|
783
|
+
- A BenchResult object with the initialized dataset
|
784
|
+
- A list of function input tuples (index, value pairs)
|
785
|
+
- A list of dimension names for the dataset
|
597
786
|
"""
|
598
|
-
|
599
787
|
if time_src is None:
|
600
788
|
time_src = datetime.now()
|
601
789
|
bench_cfg.meta_vars = self.define_extra_vars(bench_cfg, bench_cfg.repeats, time_src)
|
@@ -641,7 +829,17 @@ class Bench(BenchPlotServer):
|
|
641
829
|
|
642
830
|
return bench_res, function_inputs, dims_cfg.dims_name
|
643
831
|
|
644
|
-
def define_const_inputs(self, const_vars) -> dict:
|
832
|
+
def define_const_inputs(self, const_vars: List[Tuple[param.Parameter, Any]]) -> Optional[dict]:
|
833
|
+
"""Convert constant variable tuples into a dictionary of name-value pairs.
|
834
|
+
|
835
|
+
Args:
|
836
|
+
const_vars (List[Tuple[param.Parameter, Any]]): List of (parameter, value) tuples
|
837
|
+
representing constant parameters and their values
|
838
|
+
|
839
|
+
Returns:
|
840
|
+
Optional[dict]: Dictionary mapping parameter names to their constant values,
|
841
|
+
or None if const_vars is None
|
842
|
+
"""
|
645
843
|
constant_inputs = None
|
646
844
|
if const_vars is not None:
|
647
845
|
const_vars, constant_values = [
|
@@ -653,16 +851,22 @@ class Bench(BenchPlotServer):
|
|
653
851
|
constant_inputs = dict(zip(constant_names, constant_values))
|
654
852
|
return constant_inputs
|
655
853
|
|
656
|
-
def define_extra_vars(
|
657
|
-
|
854
|
+
def define_extra_vars(
|
855
|
+
self, bench_cfg: BenchCfg, repeats: int, time_src: datetime | str
|
856
|
+
) -> List[IntSweep]:
|
857
|
+
"""Define extra meta variables for tracking benchmark execution details.
|
858
|
+
|
859
|
+
This function creates variables that aren't passed to the worker function but are stored
|
860
|
+
in the n-dimensional array to provide context about the benchmark, such as the number of
|
861
|
+
repeat measurements and timestamps.
|
658
862
|
|
659
863
|
Args:
|
660
|
-
bench_cfg (BenchCfg):
|
661
|
-
repeats (int):
|
662
|
-
time_src (datetime): a
|
864
|
+
bench_cfg (BenchCfg): The benchmark configuration to add variables to
|
865
|
+
repeats (int): The number of times each sample point should be measured
|
866
|
+
time_src (datetime | str): Either a timestamp or a string event name for temporal tracking
|
663
867
|
|
664
868
|
Returns:
|
665
|
-
|
869
|
+
List[IntSweep]: A list of additional parameter variables to include in the benchmark
|
666
870
|
"""
|
667
871
|
bench_cfg.iv_repeat = IntSweep(
|
668
872
|
default=repeats,
|
@@ -685,16 +889,26 @@ class Bench(BenchPlotServer):
|
|
685
889
|
return extra_vars
|
686
890
|
|
687
891
|
def calculate_benchmark_results(
|
688
|
-
self,
|
892
|
+
self,
|
893
|
+
bench_cfg: BenchCfg,
|
894
|
+
time_src: datetime | str,
|
895
|
+
bench_cfg_sample_hash: str,
|
896
|
+
bench_run_cfg: BenchRunCfg,
|
689
897
|
) -> BenchResult:
|
690
|
-
"""
|
898
|
+
"""Execute the benchmark runs and collect results into an n-dimensional array.
|
899
|
+
|
900
|
+
This method handles the core benchmark execution process. It sets up the dataset,
|
901
|
+
initializes worker jobs, submits them to the sample cache for execution or retrieval,
|
902
|
+
and collects and stores the results.
|
691
903
|
|
692
904
|
Args:
|
693
|
-
bench_cfg (BenchCfg):
|
694
|
-
time_src (datetime):
|
905
|
+
bench_cfg (BenchCfg): Configuration defining the benchmark parameters
|
906
|
+
time_src (datetime | str): Timestamp or event name for the benchmark run
|
907
|
+
bench_cfg_sample_hash (str): Hash of the benchmark configuration without repeats
|
908
|
+
bench_run_cfg (BenchRunCfg): Configuration for how the benchmark should be executed
|
695
909
|
|
696
910
|
Returns:
|
697
|
-
|
911
|
+
BenchResult: An object containing all the benchmark data and results
|
698
912
|
"""
|
699
913
|
bench_res, func_inputs, dims_name = self.setup_dataset(bench_cfg, time_src)
|
700
914
|
bench_res.bench_cfg.hmap_kdims = sorted(dims_name)
|
@@ -748,6 +962,21 @@ class Bench(BenchPlotServer):
|
|
748
962
|
worker_job: WorkerJob,
|
749
963
|
bench_run_cfg: BenchRunCfg,
|
750
964
|
) -> None:
|
965
|
+
"""Store the results from a benchmark worker job into the benchmark result dataset.
|
966
|
+
|
967
|
+
This method handles unpacking the results from worker jobs and placing them
|
968
|
+
in the correct locations in the n-dimensional result dataset. It supports different
|
969
|
+
types of result variables including scalars, vectors, references, and media.
|
970
|
+
|
971
|
+
Args:
|
972
|
+
job_result (JobFuture): The future containing the worker function result
|
973
|
+
bench_res (BenchResult): The benchmark result object to store results in
|
974
|
+
worker_job (WorkerJob): The job metadata needed to index the result
|
975
|
+
bench_run_cfg (BenchRunCfg): Configuration for how results should be handled
|
976
|
+
|
977
|
+
Raises:
|
978
|
+
RuntimeError: If an unsupported result variable type is encountered
|
979
|
+
"""
|
751
980
|
result = job_result.result()
|
752
981
|
if result is not None:
|
753
982
|
logging.info(f"{job_result.job.job_id}:")
|
@@ -806,7 +1035,19 @@ class Bench(BenchPlotServer):
|
|
806
1035
|
|
807
1036
|
# bench_cfg.hmap = bench_cfg.hmaps[bench_cfg.result_hmaps[0].name]
|
808
1037
|
|
809
|
-
def init_sample_cache(self, run_cfg: BenchRunCfg):
|
1038
|
+
def init_sample_cache(self, run_cfg: BenchRunCfg) -> FutureCache:
|
1039
|
+
"""Initialize the sample cache for storing benchmark function results.
|
1040
|
+
|
1041
|
+
This method creates a FutureCache for storing and retrieving benchmark results
|
1042
|
+
based on the run configuration settings.
|
1043
|
+
|
1044
|
+
Args:
|
1045
|
+
run_cfg (BenchRunCfg): Configuration with cache settings such as overwrite policy,
|
1046
|
+
executor type, and whether to cache results
|
1047
|
+
|
1048
|
+
Returns:
|
1049
|
+
FutureCache: A configured cache for storing benchmark results
|
1050
|
+
"""
|
810
1051
|
return FutureCache(
|
811
1052
|
overwrite=run_cfg.overwrite_sample_cache,
|
812
1053
|
executor=run_cfg.executor,
|
@@ -816,23 +1057,30 @@ class Bench(BenchPlotServer):
|
|
816
1057
|
cache_results=run_cfg.cache_samples,
|
817
1058
|
)
|
818
1059
|
|
819
|
-
def clear_tag_from_sample_cache(self, tag: str, run_cfg):
|
820
|
-
"""Clear all samples from the cache that match a tag
|
1060
|
+
def clear_tag_from_sample_cache(self, tag: str, run_cfg: BenchRunCfg) -> None:
|
1061
|
+
"""Clear all samples from the cache that match a specific tag.
|
1062
|
+
|
1063
|
+
This method is useful when you want to rerun a benchmark with the same tag
|
1064
|
+
but want fresh results instead of using cached data.
|
1065
|
+
|
821
1066
|
Args:
|
822
|
-
tag(str):
|
1067
|
+
tag (str): The tag identifying samples to clear from the cache
|
1068
|
+
run_cfg (BenchRunCfg): Run configuration used to initialize the sample cache if needed
|
823
1069
|
"""
|
824
1070
|
if self.sample_cache is None:
|
825
1071
|
self.sample_cache = self.init_sample_cache(run_cfg)
|
826
1072
|
self.sample_cache.clear_tag(tag)
|
827
1073
|
|
828
1074
|
def add_metadata_to_dataset(self, bench_res: BenchResult, input_var: ParametrizedSweep) -> None:
|
829
|
-
"""
|
1075
|
+
"""Add variable metadata to the xarray dataset for improved visualization.
|
1076
|
+
|
1077
|
+
This method adds metadata like units, long names, and descriptions to the xarray dataset
|
1078
|
+
attributes, which helps visualization tools properly label axes and tooltips.
|
830
1079
|
|
831
1080
|
Args:
|
832
|
-
|
1081
|
+
bench_res (BenchResult): The benchmark result object containing the dataset to display
|
833
1082
|
input_var (ParametrizedSweep): The variable to extract metadata from
|
834
1083
|
"""
|
835
|
-
|
836
1084
|
for rv in bench_res.bench_cfg.result_vars:
|
837
1085
|
if type(rv) is ResultVar:
|
838
1086
|
bench_res.ds[rv.name].attrs["units"] = rv.units
|
@@ -851,29 +1099,113 @@ class Bench(BenchPlotServer):
|
|
851
1099
|
if input_var.__doc__ is not None:
|
852
1100
|
dsvar.attrs["description"] = input_var.__doc__
|
853
1101
|
|
854
|
-
def report_results(
|
855
|
-
|
1102
|
+
def report_results(
|
1103
|
+
self, bench_res: BenchResult, print_xarray: bool, print_pandas: bool
|
1104
|
+
) -> None:
|
1105
|
+
"""Display the calculated benchmark data in various formats.
|
1106
|
+
|
1107
|
+
This method provides options to display the benchmark results as xarray data structures
|
1108
|
+
or pandas DataFrames for debugging and inspection.
|
856
1109
|
|
857
1110
|
Args:
|
858
|
-
|
859
|
-
print_xarray (bool):
|
860
|
-
print_pandas (bool):
|
1111
|
+
bench_res (BenchResult): The benchmark result containing the dataset to display
|
1112
|
+
print_xarray (bool): If True, log the raw xarray Dataset structure
|
1113
|
+
print_pandas (bool): If True, log the dataset converted to a pandas DataFrame
|
861
1114
|
"""
|
862
1115
|
if print_xarray:
|
863
|
-
logging.info(
|
1116
|
+
logging.info(bench_res.ds)
|
864
1117
|
if print_pandas:
|
865
|
-
logging.info(
|
1118
|
+
logging.info(bench_res.ds.to_dataframe())
|
866
1119
|
|
867
1120
|
def clear_call_counts(self) -> None:
|
868
1121
|
"""Clear the worker and cache call counts, to help debug and assert caching is happening properly"""
|
869
1122
|
self.sample_cache.clear_call_counts()
|
870
1123
|
|
871
1124
|
def get_result(self, index: int = -1) -> BenchResult:
|
1125
|
+
"""Get a specific benchmark result from the results list.
|
1126
|
+
|
1127
|
+
Args:
|
1128
|
+
index (int, optional): Index of the result to retrieve. Negative indices are supported,
|
1129
|
+
with -1 (default) returning the most recent result.
|
1130
|
+
|
1131
|
+
Returns:
|
1132
|
+
BenchResult: The benchmark result at the specified index
|
1133
|
+
"""
|
872
1134
|
return self.results[index]
|
873
1135
|
|
874
1136
|
def get_ds(self, index: int = -1) -> xr.Dataset:
|
1137
|
+
"""Get the xarray Dataset from a specific benchmark result.
|
1138
|
+
|
1139
|
+
This is a convenience method that retrieves a result and returns its dataset.
|
1140
|
+
|
1141
|
+
Args:
|
1142
|
+
index (int, optional): Index of the result to retrieve the dataset from.
|
1143
|
+
Negative indices are supported, with -1 (default) returning the most recent result.
|
1144
|
+
|
1145
|
+
Returns:
|
1146
|
+
xr.Dataset: The xarray Dataset from the benchmark result
|
1147
|
+
"""
|
875
1148
|
return self.get_result(index).to_xarray()
|
876
1149
|
|
877
|
-
def publish(self, remote_callback: Callable) -> str:
|
1150
|
+
def publish(self, remote_callback: Callable[[str], str]) -> str:
|
1151
|
+
"""Publish the benchmark report to a remote location.
|
1152
|
+
|
1153
|
+
Uses the provided callback to publish the benchmark report to a remote location
|
1154
|
+
such as a GitHub Pages site.
|
1155
|
+
|
1156
|
+
Args:
|
1157
|
+
remote_callback (Callable[[str], str]): A function that takes a branch name
|
1158
|
+
and publishes the report, returning the URL where it's published
|
1159
|
+
|
1160
|
+
Returns:
|
1161
|
+
str: The URL where the report has been published
|
1162
|
+
"""
|
878
1163
|
branch_name = f"{self.bench_name}_{self.run_cfg.run_tag}"
|
879
1164
|
return self.report.publish(remote_callback, branch_name=branch_name)
|
1165
|
+
|
1166
|
+
def get_result_vars(self, as_str: bool = True) -> List[str | ParametrizedSweep]:
|
1167
|
+
"""
|
1168
|
+
Retrieve the result variables from the worker class instance.
|
1169
|
+
|
1170
|
+
Args:
|
1171
|
+
as_str (bool): If True, the result variables are returned as strings.
|
1172
|
+
If False, they are returned in their original form.
|
1173
|
+
Default is True.
|
1174
|
+
|
1175
|
+
Returns:
|
1176
|
+
List[str | ParametrizedSweep]: A list of result variables, either as strings or in their original form.
|
1177
|
+
|
1178
|
+
Raises:
|
1179
|
+
RuntimeError: If the worker class instance is not set.
|
1180
|
+
"""
|
1181
|
+
if self.worker_class_instance is not None:
|
1182
|
+
if as_str:
|
1183
|
+
return [i.name for i in self.worker_class_instance.get_results_only()]
|
1184
|
+
return self.worker_class_instance.get_results_only()
|
1185
|
+
raise RuntimeError("Worker class instance not set")
|
1186
|
+
|
1187
|
+
def to(
|
1188
|
+
self,
|
1189
|
+
result_type: BenchResult,
|
1190
|
+
result_var: Optional[Parameter] = None,
|
1191
|
+
override: bool = True,
|
1192
|
+
**kwargs: Any,
|
1193
|
+
) -> BenchResult:
|
1194
|
+
# return
|
1195
|
+
"""Return the current instance of BenchResult.
|
1196
|
+
|
1197
|
+
Returns:
|
1198
|
+
BenchResult: The current instance of the benchmark result
|
1199
|
+
"""
|
1200
|
+
return self.get_result().to(
|
1201
|
+
result_type=result_type, result_var=result_var, override=override, **kwargs
|
1202
|
+
)
|
1203
|
+
|
1204
|
+
def add(
|
1205
|
+
self,
|
1206
|
+
result_type: BenchResult,
|
1207
|
+
result_var: Optional[Parameter] = None,
|
1208
|
+
override: bool = True,
|
1209
|
+
**kwargs: Any,
|
1210
|
+
):
|
1211
|
+
self.report.append(self.to(result_type, result_var=result_var, override=override, **kwargs))
|