Home | History | Annotate | Download | only in bench
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be found
      4 # in the LICENSE file.
      5 
      6 """ Analyze recent SkPicture or Microbench data, and output suggested ranges.
      7 
      8 The outputs can be edited and pasted to bench_expectations.txt to trigger
      9 buildbot alerts if the actual benches are out of range. Details are documented
     10 in the .txt file.
     11 
     12 Currently the easiest way to batch update bench_expectations.txt is to delete
     13 all bench lines, run this script, and redirect outputs (">>") to be added to the
     14 .txt file.
     15 You can also just manually change a few lines of interest, of course.
     16 
     17 Note: since input data are stored in Google Storage, you will need to set up
     18 the corresponding library.
     19 See http://developers.google.com/storage/docs/gspythonlibrary for details.
     20 """
     21 
     22 __author__ = 'bensong (at] google.com (Ben Chen)'
     23 
     24 import bench_util
     25 import boto
     26 import cStringIO
     27 import optparse
     28 import re
     29 import shutil
     30 
     31 from oauth2_plugin import oauth2_plugin
     32 
     33 
     34 # Ratios for calculating suggested picture bench upper and lower bounds.
     35 BENCH_UB = 1.1  # Allow for 10% room for normal variance on the up side.
     36 BENCH_LB = 0.9
     37 
     38 # Further allow for a fixed amount of noise. This is especially useful for
     39 # benches of smaller absolute value. Keeping this value small will not affect
     40 # performance tunings.
     41 BENCH_ALLOWED_NOISE = 10
     42 
     43 # Name prefix for benchmark builders.
     44 BENCH_BUILDER_PREFIX = 'Perf-'
     45 
     46 # List of platforms to track. Feel free to change it to meet your needs.
     47 PLATFORMS = ['Perf-Mac10.8-MacMini4.1-GeForce320M-x86-Release',
     48              'Perf-Android-Nexus7-Tegra3-Arm7-Release',
     49              'Perf-Ubuntu12-ShuttleA-ATI5770-x86-Release',
     50              'Perf-Win7-ShuttleA-HD2000-x86-Release',
     51             ]
     52 
     53 # Filter for configs of no interest. They are old config names replaced by more
     54 # specific ones.
     55 CONFIGS_TO_FILTER = ['gpu', 'raster']
     56 
     57 # Template for gsutil uri.
     58 GOOGLE_STORAGE_URI_SCHEME = 'gs'
     59 URI_BUCKET = 'chromium-skia-gm'
     60 
     61 # Constants for optparse.
     62 USAGE_STRING = 'USAGE: %s [options]'
     63 HOWTO_STRING = """
     64 Feel free to revise PLATFORMS for your own needs. The default is the most common
     65 combination that we care most about. Platforms that did not run bench_pictures
     66 or benchmain in the given revision range will not have corresponding outputs.
     67 Please check http://go/skpbench to choose a range that fits your needs.
     68 BENCH_UB, BENCH_LB and BENCH_ALLOWED_NOISE can be changed to expand or narrow
     69 the permitted bench ranges without triggering buidbot alerts.
     70 """
     71 HELP_STRING = """
     72 Outputs expectation picture bench ranges for the latest revisions for the given
     73 revision range. For instance, --rev_range=6000:6000 will return only bench
     74 ranges for the bots that ran benches at rev 6000; --rev-range=6000:7000
     75 may have multiple bench data points for each bench configuration, and the code
     76 returns bench data for the latest revision of all available (closer to 7000).
     77 """ + HOWTO_STRING
     78 
     79 OPTION_REVISION_RANGE = '--rev-range'
     80 OPTION_REVISION_RANGE_SHORT = '-r'
     81 # Bench representation algorithm flag.
     82 OPTION_REPRESENTATION_ALG = '--algorithm'
     83 OPTION_REPRESENTATION_ALG_SHORT = '-a'
     84 # Bench type to examine. Either 'micro' or 'skp'.
     85 OPTION_BENCH_TYPE = '--bench-type'
     86 OPTION_BENCH_TYPE_SHORT = '-b'
     87 
     88 # List of valid bench types.
     89 BENCH_TYPES = ['micro', 'skp']
     90 # List of valid representation algorithms.
     91 REPRESENTATION_ALGS = ['avg', 'min', 'med', '25th']
     92 
     93 def OutputBenchExpectations(bench_type, rev_min, rev_max, representation_alg):
     94   """Reads bench data from google storage, and outputs expectations.
     95 
     96   Ignores data with revisions outside [rev_min, rev_max] integer range. For
     97   bench data with multiple revisions, we use higher revisions to calculate
     98   expected bench values.
     99   bench_type is either 'micro' or 'skp', according to the flag '-b'.
    100   Uses the provided representation_alg for calculating bench representations.
    101   """
    102   if bench_type not in BENCH_TYPES:
    103     raise Exception('Not valid bench_type! (%s)' % BENCH_TYPES)
    104   expectation_dic = {}
    105   uri = boto.storage_uri(URI_BUCKET, GOOGLE_STORAGE_URI_SCHEME)
    106   for obj in uri.get_bucket():
    107     # Filters out non-bench files.
    108     if ((not obj.name.startswith('perfdata/%s' % BENCH_BUILDER_PREFIX) and
    109          not obj.name.startswith(
    110              'playback/perfdata/%s' % BENCH_BUILDER_PREFIX)) or
    111          obj.name.find('_data') < 0):
    112       continue
    113     if ((bench_type == 'micro' and obj.name.find('_data_skp_') > 0) or
    114         (bench_type == 'skp' and obj.name.find('_skp_') < 0)):
    115       # Skips wrong bench type.
    116       continue
    117     # Ignores uninterested platforms.
    118     platform = obj.name.split('/')[1]
    119     if not platform.startswith(BENCH_BUILDER_PREFIX):
    120       platform = obj.name.split('/')[2]
    121     if not platform.startswith(BENCH_BUILDER_PREFIX):
    122       continue  # Ignores non-platform object
    123     if platform not in PLATFORMS:
    124       continue
    125     # Filters by revision.
    126     to_filter = True
    127     for rev in range(rev_min, rev_max + 1):
    128       if '_r%s_' % rev in obj.name:
    129         to_filter = False
    130         break
    131     if to_filter:
    132       continue
    133     contents = cStringIO.StringIO()
    134     obj.get_file(contents)
    135     for point in bench_util.parse('', contents.getvalue().split('\n'),
    136                                   representation_alg):
    137       if point.config in CONFIGS_TO_FILTER:
    138         continue
    139 
    140       key = '%s_%s_%s,%s-%s' % (point.bench, point.config, point.time_type,
    141                                 platform, representation_alg)
    142       # It is fine to have later revisions overwrite earlier benches, since we
    143       # only use the latest bench within revision range to set expectations.
    144       expectation_dic[key] = point.time
    145   keys = expectation_dic.keys()
    146   keys.sort()
    147   for key in keys:
    148     bench_val = expectation_dic[key]
    149     # Prints out expectation lines.
    150     print '%s,%.3f,%.3f,%.3f' % (key, bench_val,
    151                                  bench_val * BENCH_LB - BENCH_ALLOWED_NOISE,
    152                                  bench_val * BENCH_UB + BENCH_ALLOWED_NOISE)
    153 
    154 def main():
    155   """Parses flags and outputs expected Skia bench results."""
    156   parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
    157   parser.add_option(OPTION_REVISION_RANGE_SHORT, OPTION_REVISION_RANGE,
    158       dest='rev_range',
    159       help='(Mandatory) revision range separated by ":", e.g., 6000:6005')
    160   parser.add_option(OPTION_BENCH_TYPE_SHORT, OPTION_BENCH_TYPE,
    161       dest='bench_type', default='skp',
    162       help=('Bench type, either "skp" or "micro". Default to "skp".'))
    163   parser.add_option(OPTION_REPRESENTATION_ALG_SHORT, OPTION_REPRESENTATION_ALG,
    164       dest='alg', default='25th',
    165       help=('Bench representation algorithm. One of '
    166             '%s. Default to "25th".' % str(REPRESENTATION_ALGS)))
    167   (options, args) = parser.parse_args()
    168   if options.rev_range:
    169     range_match = re.search('(\d+)\:(\d+)', options.rev_range)
    170     if not range_match:
    171       parser.error('Wrong format for rev-range [%s]' % options.rev_range)
    172     else:
    173       rev_min = int(range_match.group(1))
    174       rev_max = int(range_match.group(2))
    175       OutputBenchExpectations(options.bench_type, rev_min, rev_max, options.alg)
    176   else:
    177     parser.error('Please provide mandatory flag %s' % OPTION_REVISION_RANGE)
    178 
    179 
    180 if '__main__' == __name__:
    181   main()
    182