blob: ca9023b679393c4c6df7e5e73d39d7c259d8a9a4 [file] [log] [blame]
# Copyright (c) 2012 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.
import re
import android_commands
import json
import math
# Valid values of result type.
RESULT_TYPES = {'unimportant': 'RESULT ',
'default': '*RESULT ',
'informational': '',
'unimportant-histogram': 'HISTOGRAM ',
'histogram': '*HISTOGRAM '}
def _EscapePerfResult(s):
"""Escapes |s| for use in a perf result."""
# Colons (:), equal signs (=) and slashes (/) are not allowed.
return re.sub('[\:|=/]', '_', s)
def GeomMeanAndStdDevFromHistogram(histogram_json):
histogram = json.loads(histogram_json)
count = 0
sum_of_logs = 0
for bucket in histogram['buckets']:
if 'high' in bucket:
bucket['mean'] = (bucket['low'] + bucket['high']) / 2.0
else:
bucket['mean'] = bucket['low']
if bucket['mean'] > 0:
sum_of_logs += math.log(bucket['mean']) * bucket['count']
count += bucket['count']
if count == 0:
return 0.0, 0.0
sum_of_squares = 0
geom_mean = math.exp(sum_of_logs / count)
for bucket in histogram['buckets']:
if bucket['mean'] > 0:
sum_of_squares += (bucket['mean'] - geom_mean) ** 2 * bucket['count']
return geom_mean, math.sqrt(sum_of_squares / count)
def _MeanAndStdDevFromList(values):
avg = None
sd = None
if len(values) > 1:
try:
value = '[%s]' % ','.join([str(v) for v in values])
avg = sum([float(v) for v in values]) / len(values)
sqdiffs = [(float(v) - avg) ** 2 for v in values]
variance = sum(sqdiffs) / (len(values) - 1)
sd = math.sqrt(variance)
except ValueError:
value = ", ".join(values)
else:
value = values[0]
return value, avg, sd
def PrintPerfResult(measurement, trace, values, units, result_type='default',
print_to_stdout=True):
"""Prints numerical data to stdout in the format required by perf tests.
The string args may be empty but they must not contain any colons (:) or
equals signs (=).
Args:
measurement: A description of the quantity being measured, e.g. "vm_peak".
trace: A description of the particular data point, e.g. "reference".
values: A list of numeric measured values.
units: A description of the units of measure, e.g. "bytes".
result_type: Accepts values of RESULT_TYPES.
print_to_stdout: If True, prints the output in stdout instead of returning
the output to caller.
Returns:
String of the formated perf result.
"""
assert result_type in RESULT_TYPES, 'result type: %s is invalid' % result_type
trace_name = _EscapePerfResult(trace)
if result_type in ['unimportant', 'default', 'informational']:
assert isinstance(values, list)
assert len(values)
assert '/' not in measurement
value, avg, sd = _MeanAndStdDevFromList(values)
output = '%s%s: %s%s%s %s' % (
RESULT_TYPES[result_type],
_EscapePerfResult(measurement),
trace_name,
# Do not show equal sign if the trace is empty. Usually it happens when
# measurement is enough clear to describe the result.
'= ' if trace_name else '',
value,
units)
else:
assert(result_type in ['histogram', 'unimportant-histogram'])
assert isinstance(values, list)
# The histograms can only be printed individually, there's no computation
# across different histograms.
assert len(values) == 1
value = values[0]
measurement += '.' + trace_name
output = '%s%s: %s= %s' % (
RESULT_TYPES[result_type],
_EscapePerfResult(measurement),
_EscapePerfResult(measurement),
value)
avg, sd = GeomMeanAndStdDevFromHistogram(value)
if avg:
output += '\nAvg %s: %f%s' % (measurement, avg, units)
if sd:
output += '\nSd %s: %f%s' % (measurement, sd, units)
if print_to_stdout:
print output
return output
class PerfTestSetup(object):
"""Provides methods for setting up a device for perf testing."""
_DROP_CACHES = '/proc/sys/vm/drop_caches'
_SCALING_GOVERNOR = '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor'
def __init__(self, adb):
self._adb = adb
num_cpus = self._adb.GetFileContents('/sys/devices/system/cpu/online',
log_result=False)
assert num_cpus, 'Unable to find /sys/devices/system/cpu/online'
self._num_cpus = int(num_cpus[0].split('-')[-1])
self._original_scaling_governor = None
def DropRamCaches(self):
"""Drops the filesystem ram caches for performance testing."""
if not self._adb.IsRootEnabled():
self._adb.EnableAdbRoot()
self._adb.RunShellCommand('sync')
self._adb.RunShellCommand('echo 3 > ' + PerfTestSetup._DROP_CACHES)
def SetUp(self):
"""Sets up performance tests."""
if not self._original_scaling_governor:
self._original_scaling_governor = self._adb.GetFileContents(
PerfTestSetup._SCALING_GOVERNOR % 0,
log_result=False)[0]
self._SetScalingGovernorInternal('performance')
self.DropRamCaches()
def TearDown(self):
"""Tears down performance tests."""
if self._original_scaling_governor:
self._SetScalingGovernorInternal(self._original_scaling_governor)
self._original_scaling_governor = None
def _SetScalingGovernorInternal(self, value):
for cpu in range(self._num_cpus):
self._adb.RunShellCommand(
('echo %s > ' + PerfTestSetup._SCALING_GOVERNOR) % (value, cpu))