Home | History | Annotate | Download | only in scripts
      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 #
     19 # Analysis of framestats deltas between 2+ runs
     20 # This analysis outputs potential regressions when comparing
     21 # a baseline folder output of microbenchmarks like UiBench with
     22 # a test folder with similar stats.  The base and test folders
     23 # can contain the output of one or more runs of run_uibench.py or
     24 # similar script.
     25 
     26 # For example:
     27 # 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
     28 # No handlers could be found for logger "EnergyModel"
     29 # Potential regression in:
     30 #                  avg-frame-time-50                avg-frame-time-90                avg-frame-time-95                avg-frame-time-99
     31 # UiBenchJankTests#testTrivialAnimation
     32 # 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]
     33 # 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]
     34 # ave base         [ 5.]                            [ 5.]                            [ 5.]                            [ 5.25714286]
     35 # ave test         [ 5.]                            [ 5.]                            [ 5.]                            [ 5.6]
     36 # avg delta        [ 0.]                            [ 0.]                            [ 0.]                            [ 0.34285714]
     37 
     38 import os
     39 import sys
     40 import argparse
     41 import numpy as np
     42 import pandas as pd
     43 from collections import OrderedDict
     44 from android.workloads.uibench import UiBench
     45 
     46 averages = ['avg-frame-time-50', 'avg-frame-time-90',
     47             'avg-frame-time-95', 'avg-frame-time-99']
     48 stats_file = 'framestats.txt'
     49 
     50 """
     51 Parses a directory to find stats
     52 
     53 :param stats_dir: path to stats dir
     54 :type stats_dir: str
     55 
     56 :param stats_file: name of stats file
     57 :type stats_file: str
     58 """
     59 def parse_stats_dir(stats_dir, stats_file):
     60     df = {}
     61     run_name = os.path.basename(os.path.normpath(stats_dir))
     62     for root, dirs, files in os.walk(stats_dir):
     63         if stats_file in files:
     64             test_name = os.path.basename(os.path.normpath(root))
     65             if test_name not in df:
     66                 df[test_name] = pd.DataFrame()
     67             df[test_name] = df[test_name].append(UiBench.get_results(root))
     68     return df
     69 
     70 """
     71 Prints a header line
     72 
     73 :param df: pandas dataframe to extract columns names from
     74 :type df: pd.DataFrame
     75 """
     76 def print_header(df):
     77     sys.stdout.write('{:16}'.format(''))
     78     for c in df.columns:
     79         sys.stdout.write(' {:32}'.format(str(c)))
     80     sys.stdout.write('\n')
     81 
     82 """
     83 Prints a pandas DataFrame as a row
     84 
     85 :param df: pandas dataframe to extract values from
     86 :type df: pd.DataFrame
     87 """
     88 def print_as_row(name, df):
     89     sys.stdout.write('{:16}'.format(name))
     90     for c in df.columns:
     91         sys.stdout.write(' {:32}'.format(str(df[c].values)))
     92     sys.stdout.write('\n')
     93 
     94 def main():
     95     header_printed = False
     96     pd.set_option('precision', 2)
     97     base = parse_stats_dir(args.baseline_dir, stats_file)
     98     test = parse_stats_dir(args.results_dir, stats_file)
     99     for name in base:
    100         try:
    101             # DataFrame created from the Series output of mean() needs to
    102             # be transposed to make averages be columns again
    103             ave_base = pd.DataFrame(base[name][averages].mean()).T
    104             ave_test = pd.DataFrame(test[name][averages].mean()).T
    105             if args.verbose or \
    106                ((ave_test - ave_base) > args.threshold_ave).iloc[0].any():
    107                 if not header_printed:
    108                     if not args.verbose:
    109                         print "Potential regression in:"
    110                     print_header(base[name][averages])
    111                     header_printed = True
    112                 print name
    113                 print_as_row('all base', base[name][averages])
    114                 print_as_row('all test', test[name][averages])
    115                 print_as_row('ave base', ave_base)
    116                 print_as_row('ave test', ave_test)
    117                 print_as_row('avg delta', ave_test - ave_base)
    118         except KeyError:
    119             sys.stdout.write('\n')
    120     if not header_printed:
    121         print "No regression found"
    122 
    123 if __name__ == "__main__":
    124     parser = argparse.ArgumentParser(
    125         description="Framestats analysis")
    126 
    127     parser.add_argument("--results_dir", "-d", type=str,
    128                         default=os.path.join(os.environ["LISA_HOME"],
    129                                              "results/UiBench_default"),
    130                         help="The test directory to read from. (default \
    131                         LISA_HOME/restuls/UiBench_deafult)")
    132     parser.add_argument("--baseline_dir", "-b", type=str, required=True,
    133                         help="The directory that provides baseline test to \
    134                         compare against")
    135     parser.add_argument("--threshold_ave", "-t", type=float, default=0.99,
    136                         help="Amount to filter noise in average values")
    137     parser.add_argument("--verbose", "-v", action='store_true',
    138                         help="Verbose output")
    139     args = parser.parse_args()
    140     main()
    141