diff --git a/src/performance_measurement/figures/plot.py b/src/performance_measurement/figures/plot.py index 56b9318..2825cf9 100644 --- a/src/performance_measurement/figures/plot.py +++ b/src/performance_measurement/figures/plot.py @@ -1,177 +1,183 @@ #!/usr/bin/env python """Script to visualize google-benchmark output""" from __future__ import print_function import argparse import sys import logging import json import pandas as pd import matplotlib.pyplot as plt import pathlib logging.basicConfig(format="[%(levelname)s] %(message)s") METRICS = [ "real_time", "cpu_time", "bytes_per_second", "items_per_second", "iterations", ] TRANSFORMS = {"": lambda x: x, "inverse": lambda x: 1.0 / x} def get_default_ylabel(args): """Compute default ylabel for commandline args""" label = "" if args.transform == "": label = args.metric else: label = args.transform + "(" + args.metric + ")" if args.relative_to is not None: label += " relative to %s" % args.relative_to return label def parse_args(): """Parse commandline arguments""" parser = argparse.ArgumentParser(description="Visualize google-benchmark output") parser.add_argument( "-f", metavar="FILE", type=argparse.FileType("r"), default=sys.stdin, dest="file", help="path to file containing the csv or json benchmark data", ) parser.add_argument( "-m", metavar="METRIC", choices=METRICS, default=METRICS[0], dest="metric", help="metric to plot on the y-axis, valid choices are: %s" % ", ".join(METRICS), ) parser.add_argument( "-t", metavar="TRANSFORM", choices=TRANSFORMS.keys(), default="", help="transform to apply to the chosen metric, valid choices are: %s" % ", ".join(list(TRANSFORMS)), dest="transform", ) parser.add_argument( "-r", metavar="RELATIVE_TO", type=str, default=None, dest="relative_to", help="plot metrics relative to this label", ) parser.add_argument( "--xlabel", type=str, default="input size", help="label of the x-axis" ) parser.add_argument("--ylabel", type=str, help="label of the y-axis") parser.add_argument("--title", type=str, default="", help="title of the plot") parser.add_argument( "--logx", action="store_true", help="plot x-axis on a logarithmic scale" ) parser.add_argument( "--logy", action="store_true", help="plot y-axis on a logarithmic scale" ) parser.add_argument( "--output", type=str, default="", help="File in which to save the graph" ) args = parser.parse_args() if args.ylabel is None: args.ylabel = get_default_ylabel(args) return args def parse_input_size(name): splits = name.split("/") if len(splits) == 1: return 1 return int(splits[-1]) def read_data(args): """Read and process dataframe using commandline args""" extension = pathlib.Path(args.file.name).suffix try: if extension == ".csv": data = pd.read_csv(args.file, usecols=["name", args.metric]) elif extension == ".json": json_data = json.load(args.file) data = pd.DataFrame(json_data["benchmarks"]) else: logging.error("Unsupported file extension '{}'".format(extension)) exit(1) except ValueError: logging.error( 'Could not parse the benchmark data. Did you forget "--benchmark_format=[csv|json] when running the benchmark"?' ) exit(1) data["label"] = data["name"].apply(lambda x: x.split("/")[-2]) data["input"] = data["name"].apply(parse_input_size) data[args.metric] = data[args.metric].apply(TRANSFORMS[args.transform]) return data def plot_groups(label_groups, args): """Display the processed data""" fig, ax = plt.subplots() for label, group in label_groups.items(): ax.plot( group["input"], group[args.metric] / 1024 / 1024 / 1024, label=label, marker=".", ) if args.logx: ax.set_xscale("log", base=2) if args.logy: ax.set_yscale("log") ax.set_xlabel(args.xlabel) ax.set_ylabel(args.ylabel) ax.set_title(args.title) ax.legend() - ax.vlines([32, 1024, 19712], 0, 120, color="gray") - ax.text(16, 115, "L1") - ax.text(512, 115, "L2") - ax.text(19712 / 2, 115, "L3") + # ax.vlines([32, 1024, 19712], 0, 120, color="gray") + # ax.text(16, 115, "L1") + # ax.text(512, 115, "L2") + # ax.text(19712 / 2, 115, "L3") + + caches = [48, 1280, 55296] + + ax.vlines(caches, 0, 120, color="gray") + for i, c in enumerate(caches): + ax.text(c / 2, 115, f"L{i}") if args.output: logging.info("Saving to %s" % args.output) plt.savefig(args.output) else: plt.show() def main(): """Entry point of the program""" args = parse_args() data = read_data(args) label_groups = {} for label, group in data.groupby("label"): label_groups[label] = group.set_index("input", drop=False) if args.relative_to is not None: try: baseline = label_groups[args.relative_to][args.metric].copy() except KeyError as key: msg = "Key %s is not present in the benchmark output" logging.error(msg, str(key)) exit(1) if args.relative_to is not None: for label in label_groups: label_groups[label][args.metric] /= baseline plot_groups(label_groups, args) if __name__ == "__main__": main()