PageRenderTime 25ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/bench/tile_analyze.py

https://gitlab.com/Atomic-ROM/external_skia
Python | 279 lines | 261 code | 8 blank | 10 comment | 0 complexity | 8688473ed9627e8e904e134c324a099f MD5 | raw file
  1. #!/usr/bin/env python
  2. # Copyright (c) 2013 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. """ Analyze per-tile and viewport bench data, and output visualized results.
  6. """
  7. __author__ = 'bensong@google.com (Ben Chen)'
  8. import bench_util
  9. import boto
  10. import math
  11. import optparse
  12. import os
  13. import re
  14. import shutil
  15. from oauth2_plugin import oauth2_plugin
  16. # The default platform to analyze. Used when OPTION_PLATFORM flag is not set.
  17. DEFAULT_PLATFORM = 'Nexus10_4-1_Float_Bench_32'
  18. # Template for gsutil uri.
  19. GOOGLE_STORAGE_URI_SCHEME = 'gs'
  20. URI_BUCKET = 'chromium-skia-gm'
  21. # Maximum number of rows of tiles to track for viewport covering.
  22. MAX_TILE_ROWS = 8
  23. # Constants for optparse.
  24. USAGE_STRING = 'USAGE: %s [options]'
  25. HOWTO_STRING = """
  26. Note: to read bench data stored in Google Storage, you will need to set up the
  27. corresponding Python library.
  28. See http://developers.google.com/storage/docs/gspythonlibrary for details.
  29. """
  30. HELP_STRING = """
  31. For the given platform and revision number, find corresponding viewport and
  32. tile benchmarks for each available picture bench, and output visualization and
  33. analysis in HTML. By default it reads from Skia's Google Storage location where
  34. bot data are stored, but if --dir is given, will read from local directory
  35. instead.
  36. """ + HOWTO_STRING
  37. OPTION_DIR = '--dir'
  38. OPTION_DIR_SHORT = '-d'
  39. OPTION_REVISION = '--rev'
  40. OPTION_REVISION_SHORT = '-r'
  41. OPTION_PLATFORM = '--platform'
  42. OPTION_PLATFORM_SHORT = '-p'
  43. # Bench representation algorithm flag.
  44. OPTION_REPRESENTATION_ALG = '--algorithm'
  45. OPTION_REPRESENTATION_ALG_SHORT = '-a'
  46. # Bench representation algorithm. See trunk/bench/bench_util.py.
  47. REPRESENTATION_ALG = bench_util.ALGORITHM_25TH_PERCENTILE
  48. # Constants for bench file matching.
  49. GOOGLE_STORAGE_OBJECT_NAME_PREFIX = 'perfdata/Skia_'
  50. BENCH_FILE_PREFIX_TEMPLATE = 'bench_r%s_'
  51. TILING_FILE_NAME_INDICATOR = '_tile_'
  52. VIEWPORT_FILE_NAME_INDICATOR = '_viewport_'
  53. # Regular expression for matching format '<integer>x<integer>'.
  54. DIMENSIONS_RE = '(\d+)x(\d+)'
  55. # HTML and JS output templates.
  56. HTML_PREFIX = """
  57. <html><head><script type="text/javascript" src="https://www.google.com/jsapi">
  58. </script><script type="text/javascript">google.load("visualization", "1.1",
  59. {packages:["table"]});google.load("prototype", "1.6");</script>
  60. <script type="text/javascript" src="https://systemsbiology-visualizations.googlecode.com/svn/trunk/src/main/js/load.js"></script><script
  61. type="text/javascript"> systemsbiology.load("visualization", "1.0",
  62. {packages:["bioheatmap"]});</script><script type="text/javascript">
  63. google.setOnLoadCallback(drawVisualization); function drawVisualization() {
  64. """
  65. HTML_SUFFIX = '</body></html>'
  66. BAR_CHART_TEMPLATE = ('<img src="https://chart.googleapis.com/chart?chxr=0,0,'
  67. '300&chxt=x&chbh=15,0&chs=600x150&cht=bhg&chco=80C65A,224499,FF0000,0A8C8A,'
  68. 'EBB671,DE091A,000000,00ffff&chds=a&chdl=%s&chd=t:%s" /><br>\n')
  69. DRAW_OPTIONS = ('{passThroughBlack:false,useRowLabels:false,cellWidth:30,'
  70. 'cellHeight:30}')
  71. TABLE_OPTIONS = '{showRowNumber:true,firstRowNumber:" ",sort:"disable"}'
  72. def GetFiles(rev, bench_dir, platform):
  73. """Reads in bench files of interest into a dictionary.
  74. If bench_dir is not empty, tries to read in local bench files; otherwise check
  75. Google Storage. Filters files by revision (rev) and platform, and ignores
  76. non-tile, non-viewport bench files.
  77. Outputs dictionary [filename] -> [file content].
  78. """
  79. file_dic = {}
  80. if not bench_dir:
  81. uri = boto.storage_uri(URI_BUCKET, GOOGLE_STORAGE_URI_SCHEME)
  82. # The boto API does not allow prefix/wildcard matching of Google Storage
  83. # objects. And Google Storage has a flat structure instead of being
  84. # organized in directories. Therefore, we have to scan all objects in the
  85. # Google Storage bucket to find the files we need, which is slow.
  86. # The option of implementing prefix matching as in gsutil seems to be
  87. # overkill, but gsutil does not provide an API ready for use. If speed is a
  88. # big concern, we suggest copying bot bench data from Google Storage using
  89. # gsutil and use --log_dir for fast local data reading.
  90. for obj in uri.get_bucket():
  91. # Filters out files of no interest.
  92. if (not obj.name.startswith(GOOGLE_STORAGE_OBJECT_NAME_PREFIX) or
  93. (obj.name.find(TILING_FILE_NAME_INDICATOR) < 0 and
  94. obj.name.find(VIEWPORT_FILE_NAME_INDICATOR) < 0) or
  95. obj.name.find(platform) < 0 or
  96. obj.name.find(BENCH_FILE_PREFIX_TEMPLATE % rev) < 0):
  97. continue
  98. file_dic[
  99. obj.name[obj.name.rfind('/') + 1 : ]] = obj.get_contents_as_string()
  100. else:
  101. for f in os.listdir(bench_dir):
  102. if (not os.path.isfile(os.path.join(bench_dir, f)) or
  103. (f.find(TILING_FILE_NAME_INDICATOR) < 0 and
  104. f.find(VIEWPORT_FILE_NAME_INDICATOR) < 0) or
  105. not f.startswith(BENCH_FILE_PREFIX_TEMPLATE % rev)):
  106. continue
  107. file_dic[f] = open(os.path.join(bench_dir, f)).read()
  108. if not file_dic:
  109. raise Exception('No bench file found in "%s" or Google Storage.' %
  110. bench_dir)
  111. return file_dic
  112. def GetTileMatrix(layout, tile_size, values, viewport):
  113. """For the given tile layout and per-tile bench values, returns a matrix of
  114. bench values with tiles outside the given viewport set to 0.
  115. layout, tile_size and viewport are given in string of format <w>x<h>, where
  116. <w> is viewport width or number of tile columns, and <h> is viewport height or
  117. number of tile rows. We truncate tile rows to MAX_TILE_ROWS to adjust for very
  118. long skp's.
  119. values: per-tile benches ordered row-by-row, starting from the top-left tile.
  120. Returns [sum, matrix] where sum is the total bench tile time that covers the
  121. viewport, and matrix is used for visualizing the tiles.
  122. """
  123. [tile_cols, tile_rows] = [int(i) for i in layout.split('x')]
  124. [tile_x, tile_y] = [int(i) for i in tile_size.split('x')]
  125. [viewport_x, viewport_y] = [int(i) for i in viewport.split('x')]
  126. viewport_cols = int(math.ceil(viewport_x * 1.0 / tile_x))
  127. viewport_rows = int(math.ceil(viewport_y * 1.0 / tile_y))
  128. truncated_tile_rows = min(tile_rows, MAX_TILE_ROWS)
  129. viewport_tile_sum = 0
  130. matrix = [[0 for y in range(tile_cols)] for x in range(truncated_tile_rows)]
  131. for y in range(min(viewport_cols, tile_cols)):
  132. for x in range(min(truncated_tile_rows, viewport_rows)):
  133. matrix[x][y] = values[x * tile_cols + y]
  134. viewport_tile_sum += values[x * tile_cols + y]
  135. return [viewport_tile_sum, matrix]
  136. def GetTileVisCodes(suffix, matrix):
  137. """Generates and returns strings of [js_codes, row1, row2] which are codes for
  138. visualizing the benches from the given tile config and matrix data.
  139. row1 is used for the first row of heatmaps; row2 is for corresponding tables.
  140. suffix is only used to avoid name conflicts in the whole html output.
  141. """
  142. this_js = 'var data_%s=new google.visualization.DataTable();' % suffix
  143. for i in range(len(matrix[0])):
  144. this_js += 'data_%s.addColumn("number","%s");' % (suffix, i)
  145. this_js += 'data_%s.addRows(%s);' % (suffix, str(matrix))
  146. # Adds heatmap chart.
  147. this_js += ('var heat_%s=new org.systemsbiology.visualization' % suffix +
  148. '.BioHeatMap(document.getElementById("%s"));' % suffix +
  149. 'heat_%s.draw(data_%s,%s);' % (suffix, suffix, DRAW_OPTIONS))
  150. # Adds data table chart.
  151. this_js += ('var table_%s=new google.visualization.Table(document.' % suffix +
  152. 'getElementById("t%s"));table_%s.draw(data_%s,%s);\n' % (
  153. suffix, suffix, suffix, TABLE_OPTIONS))
  154. table_row1 = '<td>%s<div id="%s"></div></td>' % (suffix, suffix)
  155. table_row2 = '<td><div id="t%s"></div></td>' % suffix
  156. return [this_js, table_row1, table_row2]
  157. def OutputTileAnalysis(rev, representation_alg, bench_dir, platform):
  158. """Reads skp bench data and outputs tile vs. viewport analysis for the given
  159. platform.
  160. Ignores data with revisions other than rev. If bench_dir is not empty, read
  161. from the local directory instead of Google Storage.
  162. Uses the provided representation_alg for calculating bench representations.
  163. Returns (js_codes, body_codes): strings of js/html codes for stats and
  164. visualization.
  165. """
  166. js_codes = ''
  167. body_codes = ('}</script></head><body>'
  168. '<h3>PLATFORM: %s REVISION: %s</h3><br>' % (platform, rev))
  169. bench_dic = {} # [bench][config] -> [layout, [values]]
  170. file_dic = GetFiles(rev, bench_dir, platform)
  171. for f in file_dic:
  172. for point in bench_util.parse('', file_dic[f].split('\n'),
  173. representation_alg):
  174. if point.time_type: # Ignores non-walltime time_type.
  175. continue
  176. bench = point.bench.replace('.skp', '')
  177. config = point.config.replace('simple_', '')
  178. components = config.split('_')
  179. if components[0] == 'viewport':
  180. bench_dic.setdefault(bench, {})[config] = [components[1], [point.time]]
  181. else: # Stores per-tile benches.
  182. bench_dic.setdefault(bench, {})[config] = [
  183. point.tile_layout, point.per_tile_values]
  184. benches = bench_dic.keys()
  185. benches.sort()
  186. for bench in benches:
  187. body_codes += '<h4>%s</h4><br><table><tr>' % bench
  188. heat_plots = '' # For table row of heatmap plots.
  189. table_plots = '' # For table row of data table plots.
  190. # For bar plot legends and values in URL string.
  191. legends = ''
  192. values = ''
  193. keys = bench_dic[bench].keys()
  194. keys.sort()
  195. if not keys[-1].startswith('viewport'): # No viewport to analyze; skip.
  196. continue
  197. else:
  198. # Extracts viewport size, which for all viewport configs is the same.
  199. viewport = bench_dic[bench][keys[-1]][0]
  200. for config in keys:
  201. [layout, value_li] = bench_dic[bench][config]
  202. if config.startswith('tile_'): # For per-tile data, visualize tiles.
  203. tile_size = config.split('_')[1]
  204. if (not re.search(DIMENSIONS_RE, layout) or
  205. not re.search(DIMENSIONS_RE, tile_size) or
  206. not re.search(DIMENSIONS_RE, viewport)):
  207. continue # Skip unrecognized formats.
  208. [viewport_tile_sum, matrix] = GetTileMatrix(
  209. layout, tile_size, value_li, viewport)
  210. values += '%s|' % viewport_tile_sum
  211. [this_js, row1, row2] = GetTileVisCodes(config + '_' + bench, matrix)
  212. heat_plots += row1
  213. table_plots += row2
  214. js_codes += this_js
  215. else: # For viewport data, there is only one element in value_li.
  216. values += '%s|' % sum(value_li)
  217. legends += '%s:%s|' % (config, sum(value_li))
  218. body_codes += (heat_plots + '</tr><tr>' + table_plots + '</tr></table>' +
  219. '<br>' + BAR_CHART_TEMPLATE % (legends[:-1], values[:-1]))
  220. return (js_codes, body_codes)
  221. def main():
  222. """Parses flags and outputs expected Skia picture bench results."""
  223. parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
  224. parser.add_option(OPTION_PLATFORM_SHORT, OPTION_PLATFORM,
  225. dest='plat', default=DEFAULT_PLATFORM,
  226. help='Platform to analyze. Set to DEFAULT_PLATFORM if not given.')
  227. parser.add_option(OPTION_REVISION_SHORT, OPTION_REVISION,
  228. dest='rev',
  229. help='(Mandatory) revision number to analyze.')
  230. parser.add_option(OPTION_DIR_SHORT, OPTION_DIR,
  231. dest='log_dir', default='',
  232. help=('(Optional) local directory where bench log files reside. If left '
  233. 'empty (by default), will try to read from Google Storage.'))
  234. parser.add_option(OPTION_REPRESENTATION_ALG_SHORT, OPTION_REPRESENTATION_ALG,
  235. dest='alg', default=REPRESENTATION_ALG,
  236. help=('Bench representation algorithm. '
  237. 'Default to "%s".' % REPRESENTATION_ALG))
  238. (options, args) = parser.parse_args()
  239. if not (options.rev and options.rev.isdigit()):
  240. parser.error('Please provide correct mandatory flag %s' % OPTION_REVISION)
  241. return
  242. rev = int(options.rev)
  243. (js_codes, body_codes) = OutputTileAnalysis(
  244. rev, options.alg, options.log_dir, options.plat)
  245. print HTML_PREFIX + js_codes + body_codes + HTML_SUFFIX
  246. if '__main__' == __name__:
  247. main()