From 34d9055c103b6424cfaa2c7a3c1a1630a58e375e Mon Sep 17 00:00:00 2001 From: Chris Dalton Date: Fri, 20 Oct 2017 09:58:32 -0600 Subject: skpbench: support pixel phone Adds a script for pixel hardware with conservatively low clocks. Bug: skia: Change-Id: I1ade703ab9f0b4aefc9cf630e3d2efb996afd69f Reviewed-on: https://skia-review.googlesource.com/62343 Reviewed-by: Eric Boren Commit-Queue: Chris Dalton --- tools/skpbench/_adb.py | 8 ++ tools/skpbench/_hardware.py | 4 - tools/skpbench/_hardware_android.py | 9 +- tools/skpbench/_hardware_nexus_6p.py | 10 +- tools/skpbench/_hardware_pixel.py | 217 +++++++++++++++++++++++++++++++++++ tools/skpbench/_hardware_pixel_c.py | 10 +- tools/skpbench/skpbench.py | 91 ++++++++------- 7 files changed, 286 insertions(+), 63 deletions(-) create mode 100644 tools/skpbench/_hardware_pixel.py (limited to 'tools/skpbench') diff --git a/tools/skpbench/_adb.py b/tools/skpbench/_adb.py index b3b10b7bad..3bf61bc86b 100644 --- a/tools/skpbench/_adb.py +++ b/tools/skpbench/_adb.py @@ -5,6 +5,7 @@ from __future__ import print_function import re +import time import subprocess import sys @@ -44,6 +45,13 @@ class Adb: def remount(self): self.__invoke('remount') + def reboot(self): + self.__is_root = None + self.shell('reboot') + self.__invoke('wait-for-device') + while '1' != self.check('getprop sys.boot_completed').strip(): + time.sleep(1) + def __echo_shell_cmd(self, cmd): escaped = [re.sub(r'([^a-zA-Z0-9])', r'\\\1', x) for x in cmd.strip().splitlines()] diff --git a/tools/skpbench/_hardware.py b/tools/skpbench/_hardware.py index de8848df78..9283243c04 100644 --- a/tools/skpbench/_hardware.py +++ b/tools/skpbench/_hardware.py @@ -41,10 +41,6 @@ class Hardware: """Prints any info that may help improve or debug hardware monitoring.""" pass - def sleep(self, sleeptime): - """Puts the hardware into a resting state for a fixed amount of time.""" - time.sleep(sleeptime) - class HardwareException(Exception): """Gets thrown when certain hardware state is not what we expect. diff --git a/tools/skpbench/_hardware_android.py b/tools/skpbench/_hardware_android.py index ebaba0ab6d..fd001b201d 100644 --- a/tools/skpbench/_hardware_android.py +++ b/tools/skpbench/_hardware_android.py @@ -20,6 +20,10 @@ class HardwareAndroid(Hardware): self._adb.check('cat /proc/sys/kernel/randomize_va_space') def __enter__(self): + Hardware.__enter__(self) + if not self._adb.is_root() and self._adb.root(): + self._adb.remount() + self._adb.shell('\n'.join([ # turn on airplane mode. ''' @@ -53,7 +57,7 @@ class HardwareAndroid(Hardware): print("WARNING: no adb root access; results may be unreliable.", file=sys.stderr) - return Hardware.__enter__(self) + return self def __exit__(self, exception_type, exception_value, traceback): Hardware.__exit__(self, exception_type, exception_value, traceback) @@ -102,6 +106,3 @@ class HardwareAndroid(Hardware): done''') Hardware.print_debug_diagnostics(self) - - def sleep(self, sleeptime): - Hardware.sleep(self, sleeptime) diff --git a/tools/skpbench/_hardware_nexus_6p.py b/tools/skpbench/_hardware_nexus_6p.py index 077933bb41..58eb52f925 100644 --- a/tools/skpbench/_hardware_nexus_6p.py +++ b/tools/skpbench/_hardware_nexus_6p.py @@ -14,13 +14,14 @@ class HardwareNexus6P(HardwareAndroid): HardwareAndroid.__init__(self, adb) def __enter__(self): + HardwareAndroid.__enter__(self) self._lock_clocks() - return HardwareAndroid.__enter__(self) + return self def __exit__(self, exception_type, exception_value, exception_traceback): + self._unlock_clocks() HardwareAndroid.__exit__(self, exception_type, exception_value, exception_traceback) - self._unlock_clocks() def _lock_clocks(self): if not self._adb.is_root(): @@ -148,8 +149,3 @@ class HardwareNexus6P(HardwareAndroid): for i in range(4, 7)] Expectation.check_all(expectations, result.splitlines()) - - def sleep(self, sleeptime): - self._unlock_clocks() - HardwareAndroid.sleep(self, sleeptime) - self._lock_clocks() diff --git a/tools/skpbench/_hardware_pixel.py b/tools/skpbench/_hardware_pixel.py new file mode 100644 index 0000000000..aa75c5f63f --- /dev/null +++ b/tools/skpbench/_hardware_pixel.py @@ -0,0 +1,217 @@ +# Copyright 2017 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +from _hardware import HardwareException, Expectation +from _hardware_android import HardwareAndroid +from collections import namedtuple +import itertools + +CPU_CLOCK_RATE = 1670400 +GPU_CLOCK_RATE = 510000000 + +DEVFREQ_DIRNAME = '/sys/class/devfreq' +DEVFREQ_THROTTLE = 0.74 +DEVFREQ_BLACKLIST = ('b00000.qcom,kgsl-3d0', 'soc:qcom,kgsl-busmon', + 'soc:qcom,m4m') +DevfreqKnobs = namedtuple('knobs', + ('available_governors', 'available_frequencies', + 'governor', 'min_freq', 'max_freq', 'cur_freq')) + +class HardwarePixel(HardwareAndroid): + def __init__(self, adb): + HardwareAndroid.__init__(self, adb) + self._discover_devfreqs() + + def __enter__(self): + HardwareAndroid.__enter__(self) + self._lock_clocks() + return self + + def __exit__(self, exception_type, exception_value, exception_traceback): + # pixel struggles waking up; just pull a hard reboot. + self._adb.reboot() + + def _lock_clocks(self): + if not self._adb.is_root(): + return + + self._adb.shell('\n'.join(['''\ + stop thermal-engine + stop thermald + stop perfd + stop mpdecision''', + + # enable and lock the two fast cores. + ''' + for N in 3 2; do + echo 1 > /sys/devices/system/cpu/cpu$N/online + echo userspace > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_governor + echo %i > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_max_freq + echo %i > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_min_freq + echo %i > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_setspeed + done''' % tuple(CPU_CLOCK_RATE for _ in range(3)), + + # turn off the two slow cores + ''' + for N in 1 0; do + echo 0 > /sys/devices/system/cpu/cpu$N/online + done''', + + # gpu perf commands from + # https://developer.qualcomm.com/qfile/28823/lm80-p0436-11_adb_commands.pdf + ''' + echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split + echo 1 > /sys/class/kgsl/kgsl-3d0/force_bus_on + echo 1 > /sys/class/kgsl/kgsl-3d0/force_rail_on + echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on + echo 1000000 > /sys/class/kgsl/kgsl-3d0/idle_timer + echo userspace > /sys/class/kgsl/kgsl-3d0/devfreq/governor + echo 2 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel + echo 2 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel + echo 2 > /sys/class/kgsl/kgsl-3d0/thermal_pwrlevel + echo %i > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq + echo %i > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq + echo %i > /sys/class/kgsl/kgsl-3d0/max_gpuclk + echo %i > /sys/class/kgsl/kgsl-3d0/gpuclk''' % + tuple(GPU_CLOCK_RATE for _ in range(4))] + \ + + self._devfreq_lock_cmds)) + + def _unlock_clocks(self): + if not self._adb.is_root(): + return + + self._adb.shell('\n'.join( + self._devfreq_unlock_cmds + [ + + # restore gpu settings to default. + ''' + echo 133000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq + echo 600000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq + echo 0 > /sys/class/kgsl/kgsl-3d0/gpuclk + echo msm-adreno-tz > /sys/class/kgsl/kgsl-3d0/devfreq/governor + echo 6 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel + echo 0 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel + echo 1 > /sys/class/kgsl/kgsl-3d0/thermal_pwrlevel + echo 0 > /sys/class/kgsl/kgsl-3d0/idle_timer + echo 0 > /sys/class/kgsl/kgsl-3d0/force_clk_on + echo 0 > /sys/class/kgsl/kgsl-3d0/force_rail_on + echo 0 > /sys/class/kgsl/kgsl-3d0/force_bus_on + echo 1 > /sys/class/kgsl/kgsl-3d0/bus_split''', + + # turn the disabled cores back on. + ''' + for N in 0 1; do + echo 1 > /sys/devices/system/cpu/cpu$N/online + done''', + + # unlock the 2 enabled big cores. + ''' + for N in 2 3; do + echo 307200 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_min_freq + echo 2150400 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_max_freq + echo 0 > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_setspeed + echo sched > /sys/devices/system/cpu/cpu$N/cpufreq/scaling_governor + done''', + + ''' + start mpdecision + start perfd + start thermald + start thermal-engine'''])) + + def sanity_check(self): + HardwareAndroid.sanity_check(self) + + if not self._adb.is_root(): + return + + result = self._adb.check(' '.join( + ['cat', + '/sys/class/power_supply/battery/capacity', + '/sys/devices/system/cpu/online'] + \ + ['/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq' % i + for i in range(2, 4)] + \ + ['/sys/class/kgsl/kgsl-3d0/thermal_pwrlevel', + '/sys/kernel/debug/clk/gpu_gx_gfx3d_clk/measure', + '/sys/kernel/debug/clk/bimc_clk/measure', + '/sys/class/thermal/thermal_zone22/temp', + '/sys/class/thermal/thermal_zone23/temp'] + \ + self._devfreq_sanity_knobs)) + + expectations = \ + [Expectation(int, min_value=30, name='battery', sleeptime=30*60), + Expectation(str, exact_value='2-3', name='online cpus')] + \ + [Expectation(int, exact_value=CPU_CLOCK_RATE, name='cpu_%i clock rate' %i) + for i in range(2, 4)] + \ + [Expectation(int, exact_value=2, name='gpu thermal power level'), + Expectation(long, min_value=(GPU_CLOCK_RATE - 5000), + max_value=(GPU_CLOCK_RATE + 5000), + name='measured gpu clock'), + Expectation(long, min_value=902390000, max_value=902409999, + name='measured ddr clock', sleeptime=10), + Expectation(int, max_value=41000, name='pm8994_tz temperature'), + Expectation(int, max_value=40, name='msm_therm temperature')] + \ + self._devfreq_sanity_expectations + + Expectation.check_all(expectations, result.splitlines()) + + def _discover_devfreqs(self): + self._devfreq_lock_cmds = list() + self._devfreq_unlock_cmds = list() + self._devfreq_sanity_knobs = list() + self._devfreq_sanity_expectations = list() + + results = iter(self._adb.check('''\ + KNOBS='%s' + for DEVICE in %s/*; do + if cd $DEVICE && ls $KNOBS >/dev/null; then + basename $DEVICE + cat $KNOBS + fi + done 2>/dev/null''' % + (' '.join(DevfreqKnobs._fields), DEVFREQ_DIRNAME)).splitlines()) + + while True: + batch = tuple(itertools.islice(results, 1 + len(DevfreqKnobs._fields))) + if not batch: + break + + devfreq = batch[0] + if devfreq in DEVFREQ_BLACKLIST: + continue + + path = '%s/%s' % (DEVFREQ_DIRNAME, devfreq) + + knobs = DevfreqKnobs(*batch[1:]) + if not 'performance' in knobs.available_governors.split(): + print('WARNING: devfreq %s does not have performance governor' % path) + continue + + self._devfreq_lock_cmds.append('echo performance > %s/governor' % path) + self._devfreq_unlock_cmds.append('echo %s > %s/governor' % + (knobs.governor, path)) + + frequencies = map(int, knobs.available_frequencies.split()) + if frequencies: + # choose the lowest frequency that is >= DEVFREQ_THROTTLE * max. + frequencies.sort() + target = DEVFREQ_THROTTLE * frequencies[-1] + idx = len(frequencies) - 1 + while idx > 0 and frequencies[idx - 1] >= target: + idx -= 1 + bench_frequency = frequencies[idx] + self._devfreq_lock_cmds.append('echo %i > %s/min_freq' % + (bench_frequency, path)) + self._devfreq_lock_cmds.append('echo %i > %s/max_freq' % + (bench_frequency, path)) + self._devfreq_unlock_cmds.append('echo %s > %s/min_freq' % + (knobs.min_freq, path)) + self._devfreq_unlock_cmds.append('echo %s > %s/max_freq' % + (knobs.max_freq, path)) + self._devfreq_sanity_knobs.append('%s/cur_freq' % path) + self._devfreq_sanity_expectations.append( + Expectation(int, exact_value=bench_frequency, + name='%s/cur_freq' % path)) diff --git a/tools/skpbench/_hardware_pixel_c.py b/tools/skpbench/_hardware_pixel_c.py index a1cd17a084..cdd9ff602f 100644 --- a/tools/skpbench/_hardware_pixel_c.py +++ b/tools/skpbench/_hardware_pixel_c.py @@ -15,13 +15,14 @@ class HardwarePixelC(HardwareAndroid): HardwareAndroid.__init__(self, adb) def __enter__(self): + HardwareAndroid.__enter__(self) self._lock_clocks() - return HardwareAndroid.__enter__(self) + return self def __exit__(self, exception_type, exception_value, exception_traceback): + self._unlock_clocks() HardwareAndroid.__exit__(self, exception_type, exception_value, exception_traceback) - self._unlock_clocks() def filter_line(self, line): JUNK = ['NvRmPrivGetChipPlatform: Could not read platform information', @@ -109,8 +110,3 @@ class HardwarePixelC(HardwareAndroid): [Expectation(str, exact_value=GPU_EMC_PROFILE, name='gpu/emc profile')] Expectation.check_all(expectations, result.splitlines()) - - def sleep(self, sleeptime): - self._unlock_clocks() - HardwareAndroid.sleep(self, sleeptime) - self._lock_clocks() diff --git a/tools/skpbench/skpbench.py b/tools/skpbench/skpbench.py index e9c7ec0f60..b8bae74660 100755 --- a/tools/skpbench/skpbench.py +++ b/tools/skpbench/skpbench.py @@ -243,45 +243,53 @@ def emit_result(line, resultsfile=None): resultsfile.flush() def run_benchmarks(configs, skps, hardware, resultsfile=None): - emit_result(SKPBench.get_header(), resultsfile) + hasheader = False benches = collections.deque([(skp, config, FLAGS.max_stddev) for skp in skps for config in configs]) while benches: - benchargs = benches.popleft() - with SKPBench(*benchargs) as skpbench: - try: - skpbench.execute(hardware) - if skpbench.best_result: - emit_result(skpbench.best_result.format(FLAGS.suffix), resultsfile) - else: - print("WARNING: no result for %s with config %s" % - (skpbench.skp, skpbench.config), file=sys.stderr) - - except StddevException: - retry_max_stddev = skpbench.max_stddev * math.sqrt(2) - if FLAGS.verbosity >= 1: - print("stddev is too high for %s/%s (%s%%, max=%.2f%%), " - "re-queuing with max=%.2f%%." % - (skpbench.best_result.config, skpbench.best_result.bench, - skpbench.best_result.stddev, skpbench.max_stddev, - retry_max_stddev), - file=sys.stderr) - benches.append((skpbench.skp, skpbench.config, retry_max_stddev, - skpbench.best_result)) - - except HardwareException as exception: - skpbench.terminate() - if FLAGS.verbosity >= 4: - hardware.print_debug_diagnostics() - if FLAGS.verbosity >= 1: - print("%s; taking a %i second nap..." % - (exception.message, exception.sleeptime), file=sys.stderr) - benches.appendleft(benchargs) # retry the same bench next time. - hardware.sleep(exception.sleeptime) - if FLAGS.verbosity >= 4: - hardware.print_debug_diagnostics() + try: + with hardware: SKPBench.run_warmup(hardware.warmup_time, configs[0]) + if not hasheader: + emit_result(SKPBench.get_header(), resultsfile) + hasheader = True + while benches: + benchargs = benches.popleft() + with SKPBench(*benchargs) as skpbench: + try: + skpbench.execute(hardware) + if skpbench.best_result: + emit_result(skpbench.best_result.format(FLAGS.suffix), + resultsfile) + else: + print("WARNING: no result for %s with config %s" % + (skpbench.skp, skpbench.config), file=sys.stderr) + + except StddevException: + retry_max_stddev = skpbench.max_stddev * math.sqrt(2) + if FLAGS.verbosity >= 1: + print("stddev is too high for %s/%s (%s%%, max=%.2f%%), " + "re-queuing with max=%.2f%%." % + (skpbench.best_result.config, skpbench.best_result.bench, + skpbench.best_result.stddev, skpbench.max_stddev, + retry_max_stddev), + file=sys.stderr) + benches.append((skpbench.skp, skpbench.config, retry_max_stddev, + skpbench.best_result)) + + except HardwareException as exception: + skpbench.terminate() + if FLAGS.verbosity >= 4: + hardware.print_debug_diagnostics() + if FLAGS.verbosity >= 1: + print("%s; exiting benchmark mode to take a %i second nap..." % + (exception.message, exception.sleeptime), file=sys.stderr) + benches.appendleft(benchargs) # retry the same bench next time. + raise # wake hw up from benchmarking mode before the nap. + + except HardwareException as exception: + time.sleep(exception.sleeptime) def main(): # Delimiter is ',' or ' ', skip if nested inside parens (e.g. gpu(a=b,c=d)). @@ -296,6 +304,9 @@ def main(): if model == 'Pixel C': from _hardware_pixel_c import HardwarePixelC hardware = HardwarePixelC(adb) + elif model == 'Pixel': + from _hardware_pixel import HardwarePixel + hardware = HardwarePixel(adb) elif model == 'Nexus 6P': from _hardware_nexus_6p import HardwareNexus6P hardware = HardwareNexus6P(adb) @@ -307,13 +318,11 @@ def main(): else: hardware = Hardware() - with hardware: - SKPBench.run_warmup(hardware.warmup_time, configs[0]) - if FLAGS.resultsfile: - with open(FLAGS.resultsfile, mode='a+') as resultsfile: - run_benchmarks(configs, skps, hardware, resultsfile=resultsfile) - else: - run_benchmarks(configs, skps, hardware) + if FLAGS.resultsfile: + with open(FLAGS.resultsfile, mode='a+') as resultsfile: + run_benchmarks(configs, skps, hardware, resultsfile=resultsfile) + else: + run_benchmarks(configs, skps, hardware) if __name__ == '__main__': -- cgit v1.2.3