summaryrefslogtreecommitdiff
path: root/systrace/catapult/devil/devil/android/perf/perf_control.py
diff options
context:
space:
mode:
Diffstat (limited to 'systrace/catapult/devil/devil/android/perf/perf_control.py')
-rw-r--r--systrace/catapult/devil/devil/android/perf/perf_control.py256
1 files changed, 56 insertions, 200 deletions
diff --git a/systrace/catapult/devil/devil/android/perf/perf_control.py b/systrace/catapult/devil/devil/android/perf/perf_control.py
index 1226be8..06a5db6 100644
--- a/systrace/catapult/devil/devil/android/perf/perf_control.py
+++ b/systrace/catapult/devil/devil/android/perf/perf_control.py
@@ -9,116 +9,6 @@ import re
from devil.android import device_errors
logger = logging.getLogger(__name__)
-_atexit_messages = set()
-
-
-# Defines how to switch between the default performance configuration
-# ('default_mode') and the mode for use when benchmarking ('high_perf_mode').
-# For devices not in the list the defaults are to set up the scaling governor to
-# 'performance' and reset it back to 'ondemand' when benchmarking is finished.
-#
-# The 'default_mode_governor' is mandatory to define, while
-# 'high_perf_mode_governor' is not taken into account. The latter is because the
-# governor 'performance' is currently used for all benchmarking on all devices.
-#
-# TODO(crbug.com/383566): Add definitions for all devices used in the perf
-# waterfall.
-_PERFORMANCE_MODE_DEFINITIONS = {
- # Fire TV Edition - 4K
- 'AFTKMST12': {
- 'default_mode_governor': 'interactive',
- },
- # Pixel 3
- 'blueline': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- # The SoC is Arm big.LITTLE. The cores 0..3 are LITTLE, the 4..7 are big.
- 'cpu_max_freq': {'0..3': 1228800, '4..7': 1536000},
- 'gpu_max_freq': 520000000,
- },
- 'default_mode': {
- 'cpu_max_freq': {'0..3': 1766400, '4..7': 2649600},
- 'gpu_max_freq': 710000000,
- },
- 'default_mode_governor': 'schedutil',
- },
- 'GT-I9300': {
- 'default_mode_governor': 'pegasusq',
- },
- 'Galaxy Nexus': {
- 'default_mode_governor': 'interactive',
- },
- # Pixel
- 'msm8996': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1209600,
- 'gpu_max_freq': 315000000,
- },
- 'default_mode': {
- # The SoC is Arm big.LITTLE. The cores 0..1 are LITTLE, the 2..3 are big.
- 'cpu_max_freq': {'0..1': 1593600, '2..3': 2150400},
- 'gpu_max_freq': 624000000,
- },
- 'default_mode_governor': 'sched',
- },
- 'Nexus 7': {
- 'default_mode_governor': 'interactive',
- },
- 'Nexus 10': {
- 'default_mode_governor': 'interactive',
- },
- 'Nexus 4': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- },
- 'default_mode_governor': 'ondemand',
- },
- 'Nexus 5': {
- # The list of possible GPU frequency values can be found in:
- # /sys/class/kgsl/kgsl-3d0/gpu_available_frequencies.
- # For CPU cores the possible frequency values are at:
- # /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1190400,
- 'gpu_max_freq': 200000000,
- },
- 'default_mode': {
- 'cpu_max_freq': 2265600,
- 'gpu_max_freq': 450000000,
- },
- 'default_mode_governor': 'ondemand',
- },
- 'Nexus 5X': {
- 'high_perf_mode': {
- 'bring_cpu_cores_online': True,
- 'cpu_max_freq': 1248000,
- 'gpu_max_freq': 300000000,
- },
- 'default_mode': {
- 'governor': 'ondemand',
- # The SoC is ARM big.LITTLE. The cores 4..5 are big, the 0..3 are LITTLE.
- 'cpu_max_freq': {'0..3': 1440000, '4..5': 1824000},
- 'gpu_max_freq': 600000000,
- },
- 'default_mode_governor': 'ondemand',
- },
-}
-
-def _GetPerfModeDefinitions(product_model):
- if product_model.startswith('AOSP on '):
- product_model = product_model.replace('AOSP on ', '')
- return _PERFORMANCE_MODE_DEFINITIONS.get(product_model)
-
-def _NoisyWarning(message):
- message += ' Results may be NOISY!!'
- logger.warning(message)
- # Add an additional warning at exit, such that it's clear that any results
- # may be different/noisy (due to the lack of intended performance mode).
- if message not in _atexit_messages:
- _atexit_messages.add(message)
- atexit.register(logger.warning, message)
class PerfControl(object):
@@ -131,10 +21,10 @@ class PerfControl(object):
def __init__(self, device):
self._device = device
- self._cpu_files = []
- for file_name in self._device.ListDirectory(self._CPU_PATH, as_root=True):
- if self._CPU_FILE_PATTERN.match(file_name):
- self._cpu_files.append(file_name)
+ self._cpu_files = [
+ filename
+ for filename in self._device.ListDirectory(self._CPU_PATH, as_root=True)
+ if self._CPU_FILE_PATTERN.match(filename)]
assert self._cpu_files, 'Failed to detect CPUs.'
self._cpu_file_list = ' '.join(self._cpu_files)
logger.info('CPUs found: %s', self._cpu_file_list)
@@ -146,73 +36,34 @@ class PerfControl(object):
(cpu, raw_governors.strip().split() if not exit_code else None)
for cpu, raw_governors, exit_code in raw]
- def _SetMaxFrequenciesFromMode(self, mode):
- """Set maximum frequencies for GPU and CPU cores.
-
- Args:
- mode: A dictionary mapping optional keys 'cpu_max_freq' and 'gpu_max_freq'
- to integer values of frequency supported by the device.
- """
- cpu_max_freq = mode.get('cpu_max_freq')
- if cpu_max_freq:
- if not isinstance(cpu_max_freq, dict):
- self._SetScalingMaxFreqForCpus(cpu_max_freq, self._cpu_file_list)
- else:
- for key, max_frequency in cpu_max_freq.iteritems():
- # Convert 'X' to 'cpuX' and 'X..Y' to 'cpuX cpu<X+1> .. cpuY'.
- if '..' in key:
- range_min, range_max = key.split('..')
- range_min, range_max = int(range_min), int(range_max)
- else:
- range_min = range_max = int(key)
- cpu_files = ['cpu%d' % number
- for number in xrange(range_min, range_max + 1)]
- # Set the |max_frequency| on requested subset of the cores.
- self._SetScalingMaxFreqForCpus(max_frequency, ' '.join(cpu_files))
- gpu_max_freq = mode.get('gpu_max_freq')
- if gpu_max_freq:
- self._SetMaxGpuClock(gpu_max_freq)
-
def SetHighPerfMode(self):
"""Sets the highest stable performance mode for the device."""
try:
self._device.EnableRoot()
except device_errors.CommandFailedError:
- _NoisyWarning('Need root for performance mode.')
+ message = 'Need root for performance mode. Results may be NOISY!!'
+ logger.warning(message)
+ # Add an additional warning at exit, such that it's clear that any results
+ # may be different/noisy (due to the lack of intended performance mode).
+ atexit.register(logger.warning, message)
return
- mode_definitions = _GetPerfModeDefinitions(self._device.product_model)
- if not mode_definitions:
- self.SetScalingGovernor('performance')
- return
- high_perf_mode = mode_definitions.get('high_perf_mode')
- if not high_perf_mode:
+
+ product_model = self._device.product_model
+ # TODO(epenner): Enable on all devices (http://crbug.com/383566)
+ if 'Nexus 4' == product_model:
+ self._ForceAllCpusOnline(True)
+ if not self._AllCpusAreOnline():
+ logger.warning('Failed to force CPUs online. Results may be NOISY!')
self.SetScalingGovernor('performance')
- return
- if high_perf_mode.get('bring_cpu_cores_online', False):
+ elif 'Nexus 5' == product_model:
self._ForceAllCpusOnline(True)
if not self._AllCpusAreOnline():
- _NoisyWarning('Failed to force CPUs online.')
- # Scaling governor must be set _after_ bringing all CPU cores online,
- # otherwise it would not affect the cores that are currently offline.
- self.SetScalingGovernor('performance')
- self._SetMaxFrequenciesFromMode(high_perf_mode)
-
- def SetDefaultPerfMode(self):
- """Sets the performance mode for the device to its default mode."""
- if not self._device.HasRoot():
- return
- mode_definitions = _GetPerfModeDefinitions(self._device.product_model)
- if not mode_definitions:
- self.SetScalingGovernor('ondemand')
+ logger.warning('Failed to force CPUs online. Results may be NOISY!')
+ self.SetScalingGovernor('performance')
+ self._SetScalingMaxFreq(1190400)
+ self._SetMaxGpuClock(200000000)
else:
- default_mode_governor = mode_definitions.get('default_mode_governor')
- assert default_mode_governor, ('Default mode governor must be provided '
- 'for all perf mode definitions.')
- self.SetScalingGovernor(default_mode_governor)
- default_mode = mode_definitions.get('default_mode')
- if default_mode:
- self._SetMaxFrequenciesFromMode(default_mode)
- self._ForceAllCpusOnline(False)
+ self.SetScalingGovernor('performance')
def SetPerfProfilingMode(self):
"""Enables all cores for reliable perf profiling."""
@@ -223,6 +74,27 @@ class PerfControl(object):
raise RuntimeError('Need root to force CPUs online.')
raise RuntimeError('Failed to force CPUs online.')
+ def SetDefaultPerfMode(self):
+ """Sets the performance mode for the device to its default mode."""
+ if not self._device.HasRoot():
+ return
+ product_model = self._device.product_model
+ if 'Nexus 5' == product_model:
+ if self._AllCpusAreOnline():
+ self._SetScalingMaxFreq(2265600)
+ self._SetMaxGpuClock(450000000)
+
+ governor_mode = {
+ 'GT-I9300': 'pegasusq',
+ 'Galaxy Nexus': 'interactive',
+ 'Nexus 4': 'ondemand',
+ 'Nexus 5': 'ondemand',
+ 'Nexus 7': 'interactive',
+ 'Nexus 10': 'interactive'
+ }.get(product_model, 'ondemand')
+ self.SetScalingGovernor(governor_mode)
+ self._ForceAllCpusOnline(False)
+
def GetCpuInfo(self):
online = (output.rstrip() == '1' and status == 0
for (_, output, status) in self._ForEachCpu('cat "$CPU/online"'))
@@ -231,24 +103,9 @@ class PerfControl(object):
in self._ForEachCpu('cat "$CPU/cpufreq/scaling_governor"'))
return zip(self._cpu_files, online, governor)
- def _ForEachCpu(self, cmd, cpu_list=None):
- """Runs a command on the device for each of the CPUs.
-
- Args:
- cmd: A string with a shell command, may may use shell expansion: "$CPU" to
- refer to the current CPU in the string form (e.g. "cpu0", "cpu1",
- and so on).
- cpu_list: A space-separated string of CPU core names, like in the example
- above
- Returns:
- A list of tuples in the form (cpu_string, command_output, exit_code), one
- tuple per each command invocation. As usual, all lines of the output
- command are joined into one line with spaces.
- """
- if cpu_list is None:
- cpu_list = self._cpu_file_list
+ def _ForEachCpu(self, cmd):
script = '; '.join([
- 'for CPU in %s' % cpu_list,
+ 'for CPU in %s' % self._cpu_file_list,
'do %s' % cmd,
'echo -n "%~%$?%~%"',
'done'
@@ -258,20 +115,20 @@ class PerfControl(object):
output = '\n'.join(output).split('%~%')
return zip(self._cpu_files, output[0::2], (int(c) for c in output[1::2]))
- def _ConditionallyWriteCpuFiles(self, path, value, cpu_files, condition):
+ def _WriteEachCpuFile(self, path, value):
+ self._ConditionallyWriteEachCpuFile(path, value, condition='true')
+
+ def _ConditionallyWriteEachCpuFile(self, path, value, condition):
template = (
'{condition} && test -e "$CPU/{path}" && echo {value} > "$CPU/{path}"')
results = self._ForEachCpu(
- template.format(path=path, value=value, condition=condition), cpu_files)
+ template.format(path=path, value=value, condition=condition))
cpus = ' '.join(cpu for (cpu, _, status) in results if status == 0)
if cpus:
logger.info('Successfully set %s to %r on: %s', path, value, cpus)
else:
logger.warning('Failed to set %s to %r on any cpus', path, value)
- def _WriteCpuFiles(self, path, value, cpu_files):
- self._ConditionallyWriteCpuFiles(path, value, cpu_files, condition='true')
-
def _ReadEachCpuFile(self, path):
return self._ForEachCpu(
'cat "$CPU/{path}"'.format(path=path))
@@ -288,8 +145,8 @@ class PerfControl(object):
condition = 'test -e "{path}" && grep -q {value} {path}'.format(
path=('${CPU}/%s' % self._AVAILABLE_GOVERNORS_REL_PATH),
value=value)
- self._ConditionallyWriteCpuFiles(
- 'cpufreq/scaling_governor', value, self._cpu_file_list, condition)
+ self._ConditionallyWriteEachCpuFile(
+ 'cpufreq/scaling_governor', value, condition)
def GetScalingGovernor(self):
"""Gets the currently set governor for each CPU.
@@ -312,8 +169,8 @@ class PerfControl(object):
"""
return self._available_governors
- def _SetScalingMaxFreqForCpus(self, value, cpu_files):
- self._WriteCpuFiles('cpufreq/scaling_max_freq', '%d' % value, cpu_files)
+ def _SetScalingMaxFreq(self, value):
+ self._WriteEachCpuFile('cpufreq/scaling_max_freq', '%d' % value)
def _SetMaxGpuClock(self, value):
self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk',
@@ -322,9 +179,8 @@ class PerfControl(object):
def _AllCpusAreOnline(self):
results = self._ForEachCpu('cat "$CPU/online"')
- # The file 'cpu0/online' is missing on some devices (example: Nexus 9). This
- # is likely because on these devices it is impossible to bring the cpu0
- # offline. Assuming the same for all devices until proven otherwise.
+ # TODO(epenner): Investigate why file may be missing
+ # (http://crbug.com/397118)
return all(output.rstrip() == '1' and status == 0
for (cpu, output, status) in results
if cpu != 'cpu0')