Home | History | Annotate | Download | only in common
      1 # Copyright 2013 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import re
      6 import sys
      7 
      8 import json
      9 import logging
     10 import math
     11 
     12 import perf_result_data_type
     13 
     14 
     15 # Mapping from result type to test output
     16 RESULT_TYPES = {perf_result_data_type.UNIMPORTANT: 'RESULT ',
     17                 perf_result_data_type.DEFAULT: '*RESULT ',
     18                 perf_result_data_type.INFORMATIONAL: '',
     19                 perf_result_data_type.UNIMPORTANT_HISTOGRAM: 'HISTOGRAM ',
     20                 perf_result_data_type.HISTOGRAM: '*HISTOGRAM '}
     21 
     22 
     23 def _EscapePerfResult(s):
     24   """Escapes |s| for use in a perf result."""
     25   return re.sub('[\:|=/#&,]', '_', s)
     26 
     27 
     28 def FlattenList(values):
     29   """Returns a simple list without sub-lists."""
     30   ret = []
     31   for entry in values:
     32     if isinstance(entry, list):
     33       ret.extend(FlattenList(entry))
     34     else:
     35       ret.append(entry)
     36   return ret
     37 
     38 
     39 def GeomMeanAndStdDevFromHistogram(histogram_json):
     40   histogram = json.loads(histogram_json)
     41   # Handle empty histograms gracefully.
     42   if not 'buckets' in histogram:
     43     return 0.0, 0.0
     44   count = 0
     45   sum_of_logs = 0
     46   for bucket in histogram['buckets']:
     47     if 'high' in bucket:
     48       bucket['mean'] = (bucket['low'] + bucket['high']) / 2.0
     49     else:
     50       bucket['mean'] = bucket['low']
     51     if bucket['mean'] > 0:
     52       sum_of_logs += math.log(bucket['mean']) * bucket['count']
     53       count += bucket['count']
     54 
     55   if count == 0:
     56     return 0.0, 0.0
     57 
     58   sum_of_squares = 0
     59   geom_mean = math.exp(sum_of_logs / count)
     60   for bucket in histogram['buckets']:
     61     if bucket['mean'] > 0:
     62       sum_of_squares += (bucket['mean'] - geom_mean) ** 2 * bucket['count']
     63   return geom_mean, math.sqrt(sum_of_squares / count)
     64 
     65 
     66 def _ValueToString(v):
     67   # Special case for floats so we don't print using scientific notation.
     68   if isinstance(v, float):
     69     return '%f' % v
     70   else:
     71     return str(v)
     72 
     73 
     74 def _MeanAndStdDevFromList(values):
     75   avg = None
     76   sd = None
     77   if len(values) > 1:
     78     try:
     79       value = '[%s]' % ','.join([_ValueToString(v) for v in values])
     80       avg = sum([float(v) for v in values]) / len(values)
     81       sqdiffs = [(float(v) - avg) ** 2 for v in values]
     82       variance = sum(sqdiffs) / (len(values) - 1)
     83       sd = math.sqrt(variance)
     84     except ValueError:
     85       value = ', '.join(values)
     86   else:
     87     value = values[0]
     88   return value, avg, sd
     89 
     90 
     91 def PrintPages(page_list):
     92   """Prints list of pages to stdout in the format required by perf tests."""
     93   print 'Pages: [%s]' % ','.join([_EscapePerfResult(p) for p in page_list])
     94 
     95 
     96 def PrintPerfResult(measurement, trace, values, units,
     97                     result_type=perf_result_data_type.DEFAULT,
     98                     print_to_stdout=True):
     99   """Prints numerical data to stdout in the format required by perf tests.
    100 
    101   The string args may be empty but they must not contain any colons (:) or
    102   equals signs (=).
    103   This is parsed by the buildbot using:
    104   http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/slave/process_log_utils.py
    105 
    106   Args:
    107     measurement: A description of the quantity being measured, e.g. "vm_peak".
    108         On the dashboard, this maps to a particular graph. Mandatory.
    109     trace: A description of the particular data point, e.g. "reference".
    110         On the dashboard, this maps to a particular "line" in the graph.
    111         Mandatory.
    112     values: A list of numeric measured values. An N-dimensional list will be
    113         flattened and treated as a simple list.
    114     units: A description of the units of measure, e.g. "bytes".
    115     result_type: Accepts values of perf_result_data_type.ALL_TYPES.
    116     print_to_stdout: If True, prints the output in stdout instead of returning
    117         the output to caller.
    118 
    119     Returns:
    120       String of the formated perf result.
    121   """
    122   assert perf_result_data_type.IsValidType(result_type), \
    123          'result type: %s is invalid' % result_type
    124 
    125   trace_name = _EscapePerfResult(trace)
    126 
    127   if (result_type == perf_result_data_type.UNIMPORTANT or
    128       result_type == perf_result_data_type.DEFAULT or
    129       result_type == perf_result_data_type.INFORMATIONAL):
    130     assert isinstance(values, list)
    131     assert '/' not in measurement
    132     flattened_values = FlattenList(values)
    133     assert len(flattened_values)
    134     value, avg, sd = _MeanAndStdDevFromList(flattened_values)
    135     output = '%s%s: %s%s%s %s' % (
    136         RESULT_TYPES[result_type],
    137         _EscapePerfResult(measurement),
    138         trace_name,
    139         # Do not show equal sign if the trace is empty. Usually it happens when
    140         # measurement is enough clear to describe the result.
    141         '= ' if trace_name else '',
    142         value,
    143         units)
    144   else:
    145     assert perf_result_data_type.IsHistogram(result_type)
    146     assert isinstance(values, list)
    147     # The histograms can only be printed individually, there's no computation
    148     # across different histograms.
    149     assert len(values) == 1
    150     value = values[0]
    151     output = '%s%s: %s= %s %s' % (
    152         RESULT_TYPES[result_type],
    153         _EscapePerfResult(measurement),
    154         trace_name,
    155         value,
    156         units)
    157     avg, sd = GeomMeanAndStdDevFromHistogram(value)
    158 
    159   if avg:
    160     output += '\nAvg %s: %f%s' % (measurement, avg, units)
    161   if sd:
    162     output += '\nSd  %s: %f%s' % (measurement, sd, units)
    163   if print_to_stdout:
    164     print output
    165     sys.stdout.flush()
    166   return output
    167