#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ RPC throughput barcharts generator (based on RPC benchmark JSON output). To obtain data used for this script use: `./tests/benchmark/rpc --benchmark_out=test.json --benchmark_out_format=json` """ import json import collections import os import argparse import numpy import shutil import matplotlib # Must set "Agg" backend before importing pyplot # This is so the script works on headless machines (without X11) matplotlib.use("Agg") import matplotlib.pyplot as plt def parse_args(): argp = argparse.ArgumentParser(description=__doc__) argp.add_argument("input", help="Load data from file.") argp.add_argument("output", help="Save plots to this directory.") argp.add_argument("--plot-width", type=int, default=1920, help="Pixel width of generated plots.") argp.add_argument("--plot-height", type=int, default=1080, help="Pixel height of generated plots.") argp.add_argument("--plot-dpi", type=int, default=96, help="DPI of generated plots.") return argp.parse_args() def humanize(num): suffix = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"] pos = 0 while num >= 1000.0: if pos == len(suffix) - 1: break num /= 1000 pos += 1 return str(int(round(num, 0))) + suffix[pos] def dehumanize(num): pos = -1 suffix = ["k", "M", "G", "T", "P", "E", "Z", "Y"] for index, suff in enumerate(suffix): if num.endswith(suff): pos = index num = num[:-1] break return int(num) * 1000 ** (pos + 1) def autolabel(ax, rects): for rect in rects: height = rect.get_height() ax.text(rect.get_x() + rect.get_width()/2., 1.00*height, humanize(height), ha="center", va="bottom") def generate_plot(size, results, plot_width, plot_height, plot_dpi, output_file): # Font size. plt.rc("font", size=10) plt.rc("axes", titlesize=24) plt.rc("axes", labelsize=16) plt.rc("xtick", labelsize=12) plt.rc("ytick", labelsize=12) plt.rc("legend", fontsize=16) plt.rc("figure", titlesize=24) groups = sorted(results.keys()) categories = list(map(lambda x: x[0], results[groups[0]])) # Plot. ind = numpy.arange(len(groups)) width = 0.10 fig, ax = plt.subplots() fig.set_size_inches(plot_width / plot_dpi, plot_height / plot_dpi) ax.set_xlabel("Concurrent threads") ax.set_ylabel("Throughput (call/s)") ax.set_facecolor("#dcdcdc") ax.set_xticks(ind + width / len(categories)) ax.set_xticklabels(groups) for line in ax.get_xgridlines(): line.set_linestyle(" ") for line in ax.get_ygridlines(): line.set_linestyle("--") ax.set_axisbelow(True) plt.grid(True) ax.set_title("RPC throughput (size: {})".format(size)) # Draw bars. rects = [] for index, category in enumerate(categories): category_results = [results[group][index][1] for group in groups] rect = ax.bar(ind + index * width, category_results, width) rects.append(rect) autolabel(ax, rect) ax.legend(rects, categories) # Plot output. plt.savefig(output_file, dpi=plot_dpi) def main(): # Read the arguments. args = parse_args() # Load data. with open(args.input) as f: data = json.load(f) # Process data. results = collections.defaultdict(lambda: collections.defaultdict(list)) for benchmark in data["benchmarks"]: info = benchmark["name"].split("/") name, size = info[0:2] threads = int(info[-1].split(":")[1]) throughput = benchmark["items_per_second"] results[size][threads].append((name, throughput)) # Cleanup output directory. if os.path.isdir(args.output): shutil.rmtree(args.output) if os.path.exists(args.output): os.remove(args.output) os.mkdir(args.output) # Generate plots. sizes = sorted(results.keys(), key=dehumanize) for prefix, size in enumerate(sizes): result = results[size] output_file = os.path.join(args.output, "{}_{}.png".format(prefix, size)) generate_plot(size, result, args.plot_width, args.plot_height, args.plot_dpi, output_file) if __name__ == "__main__": main()