1# Copyright 2015-2017 ARM Limited 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# 15 16"""Parse the results from a Workload Automation run and show it in a 17"pretty" table 18 19""" 20 21import os 22import collections, csv, re 23import pandas as pd 24from matplotlib import pyplot as plt 25 26class Result(pd.DataFrame): 27 """A DataFrame-like class for storing benchmark results""" 28 def __init__(self, *args, **kwargs): 29 super(Result, self).__init__(*args, **kwargs) 30 self.ax = None 31 32 def init_fig(self): 33 _, self.ax = plt.subplots() 34 35 def enlarge_axis(self, data): 36 """Make sure that the axis don't clobber some of the data""" 37 38 (_, _, plot_y_min, plot_y_max) = plt.axis() 39 40 concat_data = pd.concat(data[s] for s in data) 41 data_min = min(concat_data) 42 data_max = max(concat_data) 43 44 # A good margin can be 10% of the data range 45 margin = (data_max - data_min) / 10 46 if margin < 1: 47 margin = 1 48 49 update_axis = False 50 51 if data_min <= plot_y_min: 52 plot_y_min = data_min - margin 53 update_axis = True 54 55 if data_max >= plot_y_max: 56 plot_y_max = data_max + margin 57 update_axis = True 58 59 if update_axis: 60 self.ax.set_ylim(plot_y_min, plot_y_max) 61 62 def plot_results_benchmark(self, benchmark, title=None): 63 """Plot the results of the execution of a given benchmark 64 65 A title is added to the plot if title is not supplied 66 """ 67 68 if title is None: 69 title = benchmark.replace('_', ' ') 70 title = title.title() 71 72 self[benchmark].plot(ax=self.ax, kind="bar", title=title) 73 plt.legend(bbox_to_anchor=(1.05, .5), loc=6) 74 75 def plot_results(self): 76 for bench in self.columns.levels[0]: 77 self.plot_results_benchmark(bench) 78 79def get_run_number(metric): 80 found = False 81 run_number = None 82 83 if re.match("Overall_Score|score|FPS", metric): 84 found = True 85 86 match = re.search(r"(.+)[ _](\d+)", metric) 87 if match: 88 run_number = int(match.group(2)) 89 if match.group(1) == "Overall_Score": 90 run_number -= 1 91 else: 92 run_number = 0 93 94 return (found, run_number) 95 96def get_results(path=".", name=None): 97 """Return a pd.DataFrame with the results 98 99 The DataFrame's rows are the scores. The first column is the 100 benchmark name and the second the id within it. For benchmarks 101 that have a score result, that's what's used. For benchmarks with 102 FPS_* result, that's the score. E.g. glbenchmarks "score" is it's 103 fps. 104 105 An optional name argument can be passed. If supplied, it overrides 106 the name in the results file. 107 108 """ 109 110 bench_dict = collections.OrderedDict() 111 112 if os.path.isdir(path): 113 path = os.path.join(path, "results.csv") 114 115 with open(path) as fin: 116 results = csv.reader(fin) 117 118 for row in results: 119 (is_result, run_number) = get_run_number(row[3]) 120 121 if is_result: 122 if name: 123 run_id = name 124 else: 125 run_id = re.sub(r"_\d+", r"", row[0]) 126 127 bench = row[1] 128 try: 129 result = int(row[4]) 130 except ValueError: 131 result = float(row[4]) 132 133 if bench in bench_dict: 134 if run_id in bench_dict[bench]: 135 if run_number not in bench_dict[bench][run_id]: 136 bench_dict[bench][run_id][run_number] = result 137 else: 138 bench_dict[bench][run_id] = {run_number: result} 139 else: 140 bench_dict[bench] = {run_id: {run_number: result}} 141 142 bench_dfrs = {} 143 for bench, run_id_dict in bench_dict.iteritems(): 144 bench_dfrs[bench] = pd.DataFrame(run_id_dict) 145 146 return Result(pd.concat(bench_dfrs.values(), axis=1, 147 keys=bench_dfrs.keys())) 148 149def combine_results(data): 150 """Combine two DataFrame results into one 151 152 The data should be an array of results like the ones returned by 153 get_results() or have the same structure. The returned DataFrame 154 has two column indexes. The first one is the benchmark and the 155 second one is the key for the result. 156 157 """ 158 159 res_dict = {} 160 for benchmark in data[0].columns.levels[0]: 161 concat_objs = [d[benchmark] for d in data] 162 res_dict[benchmark] = pd.concat(concat_objs, axis=1) 163 164 combined = pd.concat(res_dict.values(), axis=1, keys=res_dict.keys()) 165 166 return Result(combined) 167