#!/usr/bin/env python # SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2017, ARM Limited, Google, and contributors. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Analysis of framestats deltas between 2+ runs # This analysis outputs potential regressions when comparing # a baseline folder output of microbenchmarks like UiBench with # a test folder with similar stats. The base and test folders # can contain the output of one or more runs of run_uibench.py or # similar script. # For example: # python framestats_analysis.py --baseline_dir=/usr/local/google/home/andresoportus/b/master2/external/lisa/results/UiBench_walleye_em_baseline_b/ --results_dir=/usr/local/google/home/andresoportus/b/master2/external/lisa/results/UiBench_walleye_em_first/ --threshold_ave 0.322 # No handlers could be found for logger "EnergyModel" # Potential regression in: # avg-frame-time-50 avg-frame-time-90 avg-frame-time-95 avg-frame-time-99 # UiBenchJankTests#testTrivialAnimation # all base [ 5. 5. 5. 5. 5. 5. 5.] [ 5. 5. 5. 5. 5. 5. 5.] [ 5. 5. 5. 5. 5. 5. 5.] [ 5. 5.2 5.1 5.2 5.9 5.2 5.2] # all test [ 5. 5. 5. 5. 5. 5.] [ 5. 5. 5. 5. 5. 5.] [ 5. 5. 5. 5. 5. 5.] [ 5.4 6. 5.3 5.8 6. 5.1] # ave base [ 5.] [ 5.] [ 5.] [ 5.25714286] # ave test [ 5.] [ 5.] [ 5.] [ 5.6] # avg delta [ 0.] [ 0.] [ 0.] [ 0.34285714] import os import sys import argparse import numpy as np import pandas as pd from collections import OrderedDict from android.workloads.uibench import UiBench averages = ['avg-frame-time-50', 'avg-frame-time-90', 'avg-frame-time-95', 'avg-frame-time-99'] stats_file = 'framestats.txt' """ Parses a directory to find stats :param stats_dir: path to stats dir :type stats_dir: str :param stats_file: name of stats file :type stats_file: str """ def parse_stats_dir(stats_dir, stats_file): df = {} run_name = os.path.basename(os.path.normpath(stats_dir)) for root, dirs, files in os.walk(stats_dir): if stats_file in files: test_name = os.path.basename(os.path.normpath(root)) if test_name not in df: df[test_name] = pd.DataFrame() df[test_name] = df[test_name].append(UiBench.get_results(root)) return df """ Prints a header line :param df: pandas dataframe to extract columns names from :type df: pd.DataFrame """ def print_header(df): sys.stdout.write('{:16}'.format('')) for c in df.columns: sys.stdout.write(' {:32}'.format(str(c))) sys.stdout.write('\n') """ Prints a pandas DataFrame as a row :param df: pandas dataframe to extract values from :type df: pd.DataFrame """ def print_as_row(name, df): sys.stdout.write('{:16}'.format(name)) for c in df.columns: sys.stdout.write(' {:32}'.format(str(df[c].values))) sys.stdout.write('\n') def main(): header_printed = False pd.set_option('precision', 2) base = parse_stats_dir(args.baseline_dir, stats_file) test = parse_stats_dir(args.results_dir, stats_file) for name in base: try: # DataFrame created from the Series output of mean() needs to # be transposed to make averages be columns again ave_base = pd.DataFrame(base[name][averages].mean()).T ave_test = pd.DataFrame(test[name][averages].mean()).T if args.verbose or \ ((ave_test - ave_base) > args.threshold_ave).iloc[0].any(): if not header_printed: if not args.verbose: print "Potential regression in:" print_header(base[name][averages]) header_printed = True print name print_as_row('all base', base[name][averages]) print_as_row('all test', test[name][averages]) print_as_row('ave base', ave_base) print_as_row('ave test', ave_test) print_as_row('avg delta', ave_test - ave_base) except KeyError: sys.stdout.write('\n') if not header_printed: print "No regression found" if __name__ == "__main__": parser = argparse.ArgumentParser( description="Framestats analysis") parser.add_argument("--results_dir", "-d", type=str, default=os.path.join(os.environ["LISA_HOME"], "results/UiBench_default"), help="The test directory to read from. (default \ LISA_HOME/restuls/UiBench_deafult)") parser.add_argument("--baseline_dir", "-b", type=str, required=True, help="The directory that provides baseline test to \ compare against") parser.add_argument("--threshold_ave", "-t", type=float, default=0.99, help="Amount to filter noise in average values") parser.add_argument("--verbose", "-v", action='store_true', help="Verbose output") args = parser.parse_args() main()