diff --git a/CHANGELOG.md b/CHANGELOG.md index 39cd61e..3041b5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ ## Changes -- When parameters are used with `--parameter-list` or `--parameter-scan`, the JSON export format now contains a dictionary `parameters` instead of a single key `parameter`. +- When parameters are used with `--parameter-list` or `--parameter-scan`, the JSON export format now contains a dictionary `parameters` instead of a single key `parameter`. See #253, #318. +- The `plot_parametrized.py` script now infers the parameter name, and its `--parameter-name` argument has been deprecated. See #253, #318. ## Bugfixes ## Other diff --git a/scripts/plot_parametrized.py b/scripts/plot_parametrized.py index fbe9205..48f354c 100755 --- a/scripts/plot_parametrized.py +++ b/scripts/plot_parametrized.py @@ -6,6 +6,7 @@ errorbar plot.""" import argparse import json import matplotlib.pyplot as plt +import sys parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("file", help="JSON file with benchmark results", nargs="+") @@ -13,7 +14,7 @@ parser.add_argument( "--parameter-name", metavar="name", type=str, - help="Name of the parameter / x-axis label", + help="Deprecated; parameter names are now inferred from benchmark files", ) parser.add_argument( "--log-x", help="Use a logarithmic x (parameter) axis", action="store_true" @@ -26,18 +27,66 @@ parser.add_argument( ) args = parser.parse_args() +if args.parameter_name is not None: + sys.stderr.write( + "warning: --parameter-name is deprecated; names are inferred from " + "benchmark results\n" + ) + + +def die(msg): + sys.stderr.write("fatal: %s\n" % (msg,)) + sys.exit(1) + + +def extract_parameters(results): + """Return `(parameter_name: str, parameter_values: List[float])`.""" + if not results: + die("no benchmark data to plot") + (names, values) = zip(*(unique_parameter(b) for b in results)) + names = frozenset(names) + if len(names) != 1: + die( + "benchmarks must all have the same parameter name, but found: %s" + % sorted(names) + ) + return (next(iter(names)), values) + + +def unique_parameter(benchmark): + """Return the unique parameter `(name: str, value: float)`, or dies.""" + params_dict = benchmark.get("parameters", {}) + if not params_dict: + die("benchmarks must have exactly one parameter, but found none") + if len(params_dict) > 1: + die( + "benchmarks must have exactly one parameter, but found multiple: %s" + % sorted(params_dict) + ) + return next(iter(params_dict.items())) + + +parameter_name = None for filename in args.file: with open(filename) as f: results = json.load(f)["results"] - parameter_values = [float(b["parameter"]) for b in results] + (this_parameter_name, parameter_values) = extract_parameters(results) + if parameter_name is not None and this_parameter_name != parameter_name: + die( + "files must all have the same parameter name, but found %r vs. %r" + % (parameter_name, this_parameter_name) + ) + parameter_name = this_parameter_name + + parameter_values = [float(pv) for pv in parameter_values] times_mean = [b["mean"] for b in results] times_stddev = [b["stddev"] for b in results] plt.errorbar(x=parameter_values, y=times_mean, yerr=times_stddev, capsize=2) -plt.xlabel(args.parameter_name) +plt.xlabel(parameter_name) plt.ylabel("Time [s]") if args.log_time: