diff options
Diffstat (limited to 'chromium/third_party/skia/bench/tile_analyze.py')
-rwxr-xr-x | chromium/third_party/skia/bench/tile_analyze.py | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/chromium/third_party/skia/bench/tile_analyze.py b/chromium/third_party/skia/bench/tile_analyze.py new file mode 100755 index 00000000000..03fe08673dd --- /dev/null +++ b/chromium/third_party/skia/bench/tile_analyze.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python +# Copyright (c) 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be found +# in the LICENSE file. + +""" Analyze per-tile and viewport bench data, and output visualized results. +""" + +__author__ = 'bensong@google.com (Ben Chen)' + +import bench_util +import boto +import math +import optparse +import os +import re +import shutil + +from oauth2_plugin import oauth2_plugin + +# The default platform to analyze. Used when OPTION_PLATFORM flag is not set. +DEFAULT_PLATFORM = 'Nexus10_4-1_Float_Bench_32' + +# Template for gsutil uri. +GOOGLE_STORAGE_URI_SCHEME = 'gs' +URI_BUCKET = 'chromium-skia-gm' + +# Maximum number of rows of tiles to track for viewport covering. +MAX_TILE_ROWS = 8 + +# Constants for optparse. +USAGE_STRING = 'USAGE: %s [options]' +HOWTO_STRING = """ +Note: to read bench data stored in Google Storage, you will need to set up the +corresponding Python library. +See http://developers.google.com/storage/docs/gspythonlibrary for details. +""" +HELP_STRING = """ +For the given platform and revision number, find corresponding viewport and +tile benchmarks for each available picture bench, and output visualization and +analysis in HTML. By default it reads from Skia's Google Storage location where +bot data are stored, but if --dir is given, will read from local directory +instead. +""" + HOWTO_STRING + +OPTION_DIR = '--dir' +OPTION_DIR_SHORT = '-d' +OPTION_REVISION = '--rev' +OPTION_REVISION_SHORT = '-r' +OPTION_PLATFORM = '--platform' +OPTION_PLATFORM_SHORT = '-p' +# Bench representation algorithm flag. +OPTION_REPRESENTATION_ALG = '--algorithm' +OPTION_REPRESENTATION_ALG_SHORT = '-a' + +# Bench representation algorithm. See trunk/bench/bench_util.py. +REPRESENTATION_ALG = bench_util.ALGORITHM_25TH_PERCENTILE + +# Constants for bench file matching. +GOOGLE_STORAGE_OBJECT_NAME_PREFIX = 'perfdata/Skia_' +BENCH_FILE_PREFIX_TEMPLATE = 'bench_r%s_' +TILING_FILE_NAME_INDICATOR = '_tile_' +VIEWPORT_FILE_NAME_INDICATOR = '_viewport_' + +# Regular expression for matching format '<integer>x<integer>'. +DIMENSIONS_RE = '(\d+)x(\d+)' + +# HTML and JS output templates. +HTML_PREFIX = """ +<html><head><script type="text/javascript" src="https://www.google.com/jsapi"> +</script><script type="text/javascript">google.load("visualization", "1.1", +{packages:["table"]});google.load("prototype", "1.6");</script> +<script type="text/javascript" src="https://systemsbiology-visualizations.googlecode.com/svn/trunk/src/main/js/load.js"></script><script +type="text/javascript"> systemsbiology.load("visualization", "1.0", +{packages:["bioheatmap"]});</script><script type="text/javascript"> +google.setOnLoadCallback(drawVisualization); function drawVisualization() { +""" +HTML_SUFFIX = '</body></html>' +BAR_CHART_TEMPLATE = ('<img src="https://chart.googleapis.com/chart?chxr=0,0,' + '300&chxt=x&chbh=15,0&chs=600x150&cht=bhg&chco=80C65A,224499,FF0000,0A8C8A,' + 'EBB671,DE091A,000000,00ffff&chds=a&chdl=%s&chd=t:%s" /><br>\n') +DRAW_OPTIONS = ('{passThroughBlack:false,useRowLabels:false,cellWidth:30,' + 'cellHeight:30}') +TABLE_OPTIONS = '{showRowNumber:true,firstRowNumber:" ",sort:"disable"}' + +def GetFiles(rev, bench_dir, platform): + """Reads in bench files of interest into a dictionary. + + If bench_dir is not empty, tries to read in local bench files; otherwise check + Google Storage. Filters files by revision (rev) and platform, and ignores + non-tile, non-viewport bench files. + Outputs dictionary [filename] -> [file content]. + """ + file_dic = {} + if not bench_dir: + uri = boto.storage_uri(URI_BUCKET, GOOGLE_STORAGE_URI_SCHEME) + # The boto API does not allow prefix/wildcard matching of Google Storage + # objects. And Google Storage has a flat structure instead of being + # organized in directories. Therefore, we have to scan all objects in the + # Google Storage bucket to find the files we need, which is slow. + # The option of implementing prefix matching as in gsutil seems to be + # overkill, but gsutil does not provide an API ready for use. If speed is a + # big concern, we suggest copying bot bench data from Google Storage using + # gsutil and use --log_dir for fast local data reading. + for obj in uri.get_bucket(): + # Filters out files of no interest. + if (not obj.name.startswith(GOOGLE_STORAGE_OBJECT_NAME_PREFIX) or + (obj.name.find(TILING_FILE_NAME_INDICATOR) < 0 and + obj.name.find(VIEWPORT_FILE_NAME_INDICATOR) < 0) or + obj.name.find(platform) < 0 or + obj.name.find(BENCH_FILE_PREFIX_TEMPLATE % rev) < 0): + continue + file_dic[ + obj.name[obj.name.rfind('/') + 1 : ]] = obj.get_contents_as_string() + else: + for f in os.listdir(bench_dir): + if (not os.path.isfile(os.path.join(bench_dir, f)) or + (f.find(TILING_FILE_NAME_INDICATOR) < 0 and + f.find(VIEWPORT_FILE_NAME_INDICATOR) < 0) or + not f.startswith(BENCH_FILE_PREFIX_TEMPLATE % rev)): + continue + file_dic[f] = open(os.path.join(bench_dir, f)).read() + + if not file_dic: + raise Exception('No bench file found in "%s" or Google Storage.' % + bench_dir) + + return file_dic + +def GetTileMatrix(layout, tile_size, values, viewport): + """For the given tile layout and per-tile bench values, returns a matrix of + bench values with tiles outside the given viewport set to 0. + + layout, tile_size and viewport are given in string of format <w>x<h>, where + <w> is viewport width or number of tile columns, and <h> is viewport height or + number of tile rows. We truncate tile rows to MAX_TILE_ROWS to adjust for very + long skp's. + + values: per-tile benches ordered row-by-row, starting from the top-left tile. + + Returns [sum, matrix] where sum is the total bench tile time that covers the + viewport, and matrix is used for visualizing the tiles. + """ + [tile_cols, tile_rows] = [int(i) for i in layout.split('x')] + [tile_x, tile_y] = [int(i) for i in tile_size.split('x')] + [viewport_x, viewport_y] = [int(i) for i in viewport.split('x')] + viewport_cols = int(math.ceil(viewport_x * 1.0 / tile_x)) + viewport_rows = int(math.ceil(viewport_y * 1.0 / tile_y)) + truncated_tile_rows = min(tile_rows, MAX_TILE_ROWS) + + viewport_tile_sum = 0 + matrix = [[0 for y in range(tile_cols)] for x in range(truncated_tile_rows)] + for y in range(min(viewport_cols, tile_cols)): + for x in range(min(truncated_tile_rows, viewport_rows)): + matrix[x][y] = values[x * tile_cols + y] + viewport_tile_sum += values[x * tile_cols + y] + + return [viewport_tile_sum, matrix] + +def GetTileVisCodes(suffix, matrix): + """Generates and returns strings of [js_codes, row1, row2] which are codes for + visualizing the benches from the given tile config and matrix data. + row1 is used for the first row of heatmaps; row2 is for corresponding tables. + suffix is only used to avoid name conflicts in the whole html output. + """ + this_js = 'var data_%s=new google.visualization.DataTable();' % suffix + for i in range(len(matrix[0])): + this_js += 'data_%s.addColumn("number","%s");' % (suffix, i) + this_js += 'data_%s.addRows(%s);' % (suffix, str(matrix)) + # Adds heatmap chart. + this_js += ('var heat_%s=new org.systemsbiology.visualization' % suffix + + '.BioHeatMap(document.getElementById("%s"));' % suffix + + 'heat_%s.draw(data_%s,%s);' % (suffix, suffix, DRAW_OPTIONS)) + # Adds data table chart. + this_js += ('var table_%s=new google.visualization.Table(document.' % suffix + + 'getElementById("t%s"));table_%s.draw(data_%s,%s);\n' % ( + suffix, suffix, suffix, TABLE_OPTIONS)) + table_row1 = '<td>%s<div id="%s"></div></td>' % (suffix, suffix) + table_row2 = '<td><div id="t%s"></div></td>' % suffix + + return [this_js, table_row1, table_row2] + +def OutputTileAnalysis(rev, representation_alg, bench_dir, platform): + """Reads skp bench data and outputs tile vs. viewport analysis for the given + platform. + + Ignores data with revisions other than rev. If bench_dir is not empty, read + from the local directory instead of Google Storage. + Uses the provided representation_alg for calculating bench representations. + + Returns (js_codes, body_codes): strings of js/html codes for stats and + visualization. + """ + js_codes = '' + body_codes = ('}</script></head><body>' + '<h3>PLATFORM: %s REVISION: %s</h3><br>' % (platform, rev)) + bench_dic = {} # [bench][config] -> [layout, [values]] + file_dic = GetFiles(rev, bench_dir, platform) + for f in file_dic: + for point in bench_util.parse('', file_dic[f].split('\n'), + representation_alg): + if point.time_type: # Ignores non-walltime time_type. + continue + bench = point.bench.replace('.skp', '') + config = point.config.replace('simple_', '') + components = config.split('_') + if components[0] == 'viewport': + bench_dic.setdefault(bench, {})[config] = [components[1], [point.time]] + else: # Stores per-tile benches. + bench_dic.setdefault(bench, {})[config] = [ + point.tile_layout, point.per_tile_values] + benches = bench_dic.keys() + benches.sort() + for bench in benches: + body_codes += '<h4>%s</h4><br><table><tr>' % bench + heat_plots = '' # For table row of heatmap plots. + table_plots = '' # For table row of data table plots. + # For bar plot legends and values in URL string. + legends = '' + values = '' + keys = bench_dic[bench].keys() + keys.sort() + if not keys[-1].startswith('viewport'): # No viewport to analyze; skip. + continue + else: + # Extracts viewport size, which for all viewport configs is the same. + viewport = bench_dic[bench][keys[-1]][0] + for config in keys: + [layout, value_li] = bench_dic[bench][config] + if config.startswith('tile_'): # For per-tile data, visualize tiles. + tile_size = config.split('_')[1] + if (not re.search(DIMENSIONS_RE, layout) or + not re.search(DIMENSIONS_RE, tile_size) or + not re.search(DIMENSIONS_RE, viewport)): + continue # Skip unrecognized formats. + [viewport_tile_sum, matrix] = GetTileMatrix( + layout, tile_size, value_li, viewport) + values += '%s|' % viewport_tile_sum + [this_js, row1, row2] = GetTileVisCodes(config + '_' + bench, matrix) + heat_plots += row1 + table_plots += row2 + js_codes += this_js + else: # For viewport data, there is only one element in value_li. + values += '%s|' % sum(value_li) + legends += '%s:%s|' % (config, sum(value_li)) + body_codes += (heat_plots + '</tr><tr>' + table_plots + '</tr></table>' + + '<br>' + BAR_CHART_TEMPLATE % (legends[:-1], values[:-1])) + + return (js_codes, body_codes) + +def main(): + """Parses flags and outputs expected Skia picture bench results.""" + parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING) + parser.add_option(OPTION_PLATFORM_SHORT, OPTION_PLATFORM, + dest='plat', default=DEFAULT_PLATFORM, + help='Platform to analyze. Set to DEFAULT_PLATFORM if not given.') + parser.add_option(OPTION_REVISION_SHORT, OPTION_REVISION, + dest='rev', + help='(Mandatory) revision number to analyze.') + parser.add_option(OPTION_DIR_SHORT, OPTION_DIR, + dest='log_dir', default='', + help=('(Optional) local directory where bench log files reside. If left ' + 'empty (by default), will try to read from Google Storage.')) + parser.add_option(OPTION_REPRESENTATION_ALG_SHORT, OPTION_REPRESENTATION_ALG, + dest='alg', default=REPRESENTATION_ALG, + help=('Bench representation algorithm. ' + 'Default to "%s".' % REPRESENTATION_ALG)) + (options, args) = parser.parse_args() + if not (options.rev and options.rev.isdigit()): + parser.error('Please provide correct mandatory flag %s' % OPTION_REVISION) + return + rev = int(options.rev) + (js_codes, body_codes) = OutputTileAnalysis( + rev, options.alg, options.log_dir, options.plat) + print HTML_PREFIX + js_codes + body_codes + HTML_SUFFIX + + +if '__main__' == __name__: + main() |