1#!/usr/bin/env python 2# SPDX-License-Identifier: Apache-2.0 3# 4# Copyright (C) 2017, ARM Limited, Google, and contributors. 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); you may 7# not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19from trace import Trace 20 21import trappy 22import numpy as np 23import matplotlib.pyplot as plt 24import pandas as pd 25 26import re 27import argparse 28 29class BinderThroughputAnalysis: 30 """ 31 For deserializing and plotting results obtained from binderThroughputTest 32 """ 33 34 def __init__(self, index): 35 self.latency = [] 36 self.throughput = [] 37 self.labels = [] 38 self.index = index 39 40 def add_data(self, label, latency, throughput): 41 """ 42 Append the latency and throughput measurements of one kernel 43 image to the global dataframe. 44 45 :param label: identifier of the kernel image this data is collected from 46 :type label: string 47 48 :param latency: latency measurements of a series of experiments 49 :type latency: list of floats 50 51 :param throughput: throughput measurements of a series of experiments 52 :type throughput: list of floats 53 """ 54 self.latency.append(latency) 55 self.throughput.append(throughput) 56 self.labels.append(label) 57 58 def _dfg_latency_df(self): 59 np_latency = np.array(self.latency) 60 data = np.transpose(np_latency.reshape( 61 len(self.labels), len(self.latency[0]))) 62 return pd.DataFrame(data, columns=self.labels, index=self.index) 63 64 def _dfg_throughput_df(self): 65 np_throughput = np.array(self.throughput) 66 data = np.transpose(np_throughput.reshape( 67 len(self.labels),len(self.throughput[0]))) 68 return pd.DataFrame(data, columns=self.labels, index=self.index) 69 70 def write_to_file(self, path): 71 """ 72 Write the result dataframe of combining multiple kernel images to file. 73 Example dataframe: 74 kernel1_cs_4096 kernel2_cs_4096 75 1 35.7657 35.3081 76 2 37.3145 35.7540 77 3 39.4055 39.0940 78 4 44.4658 40.3857 79 5 55.9990 51.6852 80 81 :param path: the file to write the result dataframe to 82 :type path: string 83 """ 84 with open(path, 'w') as f: 85 f.write(self._dfg_latency_df().to_string()) 86 f.write('\n\n') 87 f.write(self._dfg_throughput_df().to_string()) 88 f.write('\n\n') 89 90 def _plot(self, xlabel, ylabel): 91 plt.xlabel(xlabel) 92 plt.ylabel(ylabel) 93 ax = plt.gca() 94 ax.set_ylim(ymin=0) 95 plt.show() 96 97 def plot_latency(self, xlabel, ylabel): 98 self._dfg_latency_df().plot(rot=0) 99 self._plot(xlabel, ylabel) 100 101 def plot_throughput(self, xlabel, ylabel): 102 self._dfg_throughput_df().plot(rot=0) 103 self._plot(xlabel, ylabel) 104 105def deserialize(stream): 106 """ 107 Convert stdout from running binderThroughputTest into a list 108 of [avg_latency, iters] pair. 109 """ 110 result = {} 111 lines = stream.split("\n") 112 for l in lines: 113 if "average" in l: 114 latencies = re.findall("\d+\.\d+|\d+", l) 115 result["avg_latency"] = float(latencies[0])*1000 116 if "iterations" in l: 117 result["iters"] = float(l.split()[-1]) 118 return result 119 120parser = argparse.ArgumentParser( 121 description="Visualize latency and throughput across" 122 "kernel images given binderThroughputTest output.") 123 124parser.add_argument("--test", "-t", type=str, 125 choices=["cs", "payload"], 126 default="cs", 127 help="cs: vary number of cs_pairs while control payload.\n" 128 "payload: vary payload size, control number of cs_pairs.") 129 130parser.add_argument("--paths", "-p", type=str, nargs='+', 131 help="Paths to files to read test output from.") 132 133parser.add_argument("--out_file", "-o", type=str, 134 help="Out file to save dataframes.") 135 136parser.add_argument("--cs_pairs", type=int, nargs='?', 137 default=[1,2,3,4,5], 138 help="Vary client-server pairs as index.") 139 140parser.add_argument("--payloads", type=int, nargs='?', 141 default=[0, 4096, 4096*2, 4096*4, 4096*8], 142 help="Vary payloads as index.") 143 144if __name__ == "__main__": 145 args = parser.parse_args() 146 147 if args.test == "cs": 148 analysis = BinderThroughputAnalysis(args.cs_pairs) 149 else: 150 analysis = BinderThroughputAnalysis(args.payloads) 151 152 for path in args.paths: 153 with open(path, 'r') as f: 154 results = f.read().split("\n\n")[:-1] 155 results = list(map(deserialize, results)) 156 157 latency = [r["avg_latency"] for r in results] 158 throughput = [r["iters"] for r in results] 159 analysis.add_data(path.split('/')[-1], latency, throughput) 160 161 analysis.write_to_file(args.out_file) 162 analysis.plot_latency(args.test, "latency (microseconds") 163 analysis.plot_throughput(args.test, "iterations/sec") 164