Bug 1646368 - Add performance tuning feature to android layer. r=mozperftest-reviewers,tarek

This patch ports the performance tuning functionality from Raptor to mozperftest and adds a small test for it.

Differential Revision: https://phabricator.services.mozilla.com/D80168
This commit is contained in:
Gregory Mierzwinski 2020-06-18 22:37:27 +00:00
Родитель 966189d812
Коммит b1676248ea
4 изменённых файлов: 306 добавлений и 0 удалений

Просмотреть файл

@ -8,6 +8,7 @@ from pathlib import Path
import mozlog
from mozdevice import ADBDevice, ADBError
from mozperftest.layers import Layer
from mozperftest.system.android_perf_tuner import tune_performance
from mozperftest.utils import download_file
@ -85,6 +86,14 @@ class AndroidDevice(Layer):
"default": None,
"help": "Captures the logcat to the provided path.",
},
"perf-tuning": {
"action": "store_true",
"default": False,
"help": (
"If set, device will be tuned for performance. "
"This helps with decreasing the noise."
),
},
"intent": {"type": str, "default": None, "help": "Intent to use"},
"activity": {"type": str, "default": None, "help": "Activity to use"},
"install-apk": {
@ -183,6 +192,9 @@ class AndroidDevice(Layer):
if not self.device.is_app_installed(self.app_name):
raise Exception("%s is not installed" % self.app_name)
if self.get_arg("android-perf-tuning", False):
tune_performance(self.device)
# set up default activity with the app name if none given
if self.android_activity is None:
# guess the activity, given the app

Просмотреть файл

@ -0,0 +1,222 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
def tune_performance(device, log=None, timeout=None):
"""Set various performance-oriented parameters, to reduce jitter.
This includes some device-specific kernel tweaks.
For more information, see https://bugzilla.mozilla.org/show_bug.cgi?id=1547135.
"""
PerformanceTuner(device, log=log, timeout=timeout).tune_performance()
class PerformanceTuner:
def __init__(self, device, log=None, timeout=None):
self.device = device
self.log = log is not None and log or self.device._logger
self.timeout = timeout
def tune_performance(self):
self.log.info("tuning android device performance")
self.set_svc_power_stayon()
if self.device._have_su or self.device._have_android_su:
device_name = self.device.shell_output(
"getprop ro.product.model", timeout=self.timeout
)
# all commands require root shell from here on
self.set_scheduler()
self.set_virtual_memory_parameters()
self.turn_off_services()
self.set_cpu_performance_parameters(device_name)
self.set_gpu_performance_parameters(device_name)
self.set_kernel_performance_parameters()
self.device.clear_logcat(timeout=self.timeout)
self.log.info("android device performance tuning complete")
def _set_value_and_check_exitcode(self, file_name, value, root=False):
self.log.info("setting {} to {}".format(file_name, value))
if self.device.shell_bool(
" ".join(["echo", str(value), ">", str(file_name)]),
root=root,
timeout=self.timeout,
):
self.log.info("successfully set {} to {}".format(file_name, value))
else:
self.log.warning("command failed")
def set_svc_power_stayon(self):
self.log.info("set device to stay awake on usb")
self.device.shell_bool("svc power stayon usb", timeout=self.timeout)
def set_scheduler(self):
self.log.info("setting scheduler to noop")
scheduler_location = "/sys/block/sda/queue/scheduler"
self._set_value_and_check_exitcode(scheduler_location, "noop")
def turn_off_services(self):
services = [
"mpdecision",
"thermal-engine",
"thermald",
]
for service in services:
self.log.info(" ".join(["turning off service:", service]))
self.device.shell_bool(
" ".join(["stop", service]), root=True, timeout=self.timeout
)
services_list_output = self.device.shell_output(
"service list", timeout=self.timeout
)
for service in services:
if service not in services_list_output:
self.log.info(" ".join(["successfully terminated:", service]))
else:
self.log.warning(" ".join(["failed to terminate:", service]))
def disable_animations(self):
self.log.info("disabling animations")
commands = {
"animator_duration_scale": 0.0,
"transition_animation_scale": 0.0,
"window_animation_scale": 0.0,
}
for key, value in commands.items():
command = " ".join(["settings", "put", "global", key, str(value)])
self.log.info("setting {} to {}".format(key, value))
self.device.shell_bool(command, timeout=self.timeout)
def restore_animations(self):
# animation settings are not restored to default by reboot
self.log.info("restoring animations")
commands = {
"animator_duration_scale": 1.0,
"transition_animation_scale": 1.0,
"window_animation_scale": 1.0,
}
for key, value in commands.items():
command = " ".join(["settings", "put", "global", key, str(value)])
self.device.shell_bool(command, timeout=self.timeout)
def set_virtual_memory_parameters(self):
self.log.info("setting virtual memory parameters")
commands = {
"/proc/sys/vm/swappiness": 0,
"/proc/sys/vm/dirty_ratio": 85,
"/proc/sys/vm/dirty_background_ratio": 70,
}
for key, value in commands.items():
self._set_value_and_check_exitcode(key, value, root=True)
def set_cpu_performance_parameters(self, device_name=None):
self.log.info("setting cpu performance parameters")
commands = {}
if device_name is not None:
device_name = self.device.shell_output(
"getprop ro.product.model", timeout=self.timeout
)
if device_name == "Pixel 2":
# MSM8998 (4x 2.35GHz, 4x 1.9GHz)
# values obtained from:
# /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies
# /sys/devices/system/cpu/cpufreq/policy4/scaling_available_frequencies
commands.update(
{
"/sys/devices/system/cpu/cpufreq/policy0/scaling_governor": "performance",
"/sys/devices/system/cpu/cpufreq/policy4/scaling_governor": "performance",
"/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq": "1900800",
"/sys/devices/system/cpu/cpufreq/policy4/scaling_min_freq": "2457600",
}
)
elif device_name == "Moto G (5)":
# MSM8937(8x 1.4GHz)
# values obtained from:
# /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies
for x in range(0, 8):
commands.update(
{
"/sys/devices/system/cpu/cpu{}/"
"cpufreq/scaling_governor".format(x): "performance",
"/sys/devices/system/cpu/cpu{}/"
"cpufreq/scaling_min_freq".format(x): "1401000",
}
)
else:
self.log.info(
"CPU for device with ro.product.model '{}' unknown, not scaling_governor".format(
device_name
)
)
for key, value in commands.items():
self._set_value_and_check_exitcode(key, value, root=True)
def set_gpu_performance_parameters(self, device_name=None):
self.log.info("setting gpu performance parameters")
commands = {
"/sys/class/kgsl/kgsl-3d0/bus_split": "0",
"/sys/class/kgsl/kgsl-3d0/force_bus_on": "1",
"/sys/class/kgsl/kgsl-3d0/force_rail_on": "1",
"/sys/class/kgsl/kgsl-3d0/force_clk_on": "1",
"/sys/class/kgsl/kgsl-3d0/force_no_nap": "1",
"/sys/class/kgsl/kgsl-3d0/idle_timer": "1000000",
}
if not device_name:
device_name = self.device.shell_output(
"getprop ro.product.model", timeout=self.timeout
)
if device_name == "Pixel 2":
# Adreno 540 (710MHz)
# values obtained from:
# /sys/devices/soc/5000000.qcom,kgsl-3d0/kgsl/kgsl-3d0/max_clk_mhz
commands.update(
{
"/sys/devices/soc/5000000.qcom,kgsl-3d0/devfreq/"
"5000000.qcom,kgsl-3d0/governor": "performance",
"/sys/devices/soc/soc:qcom,kgsl-busmon/devfreq/"
"soc:qcom,kgsl-busmon/governor": "performance",
"/sys/devices/soc/5000000.qcom,kgsl-3d0/kgsl/kgsl-3d0/min_clock_mhz": "710",
}
)
elif device_name == "Moto G (5)":
# Adreno 505 (450MHz)
# values obtained from:
# /sys/devices/soc/1c00000.qcom,kgsl-3d0/kgsl/kgsl-3d0/max_clock_mhz
commands.update(
{
"/sys/devices/soc/1c00000.qcom,kgsl-3d0/devfreq/"
"1c00000.qcom,kgsl-3d0/governor": "performance",
"/sys/devices/soc/1c00000.qcom,kgsl-3d0/kgsl/kgsl-3d0/min_clock_mhz": "450",
}
)
else:
self.log.info(
"GPU for device with ro.product.model '{}' unknown, not setting devfreq".format(
device_name
)
)
for key, value in commands.items():
self._set_value_and_check_exitcode(key, value, root=True)
def set_kernel_performance_parameters(self):
self.log.info("setting kernel performance parameters")
commands = {
"/sys/kernel/debug/msm-bus-dbg/shell-client/update_request": "1",
"/sys/kernel/debug/msm-bus-dbg/shell-client/mas": "1",
"/sys/kernel/debug/msm-bus-dbg/shell-client/ab": "0",
"/sys/kernel/debug/msm-bus-dbg/shell-client/slv": "512",
}
for key, value in commands.items():
self._set_value_and_check_exitcode(key, value, root=True)

Просмотреть файл

@ -6,12 +6,25 @@ from unittest import mock
from mozperftest.tests.support import get_running_env, requests_content, temp_file
from mozperftest.environment import SYSTEM
from mozperftest.system.android import DeviceError
from mozperftest.system.android_perf_tuner import PerformanceTuner
from mozperftest.utils import silence
class FakeDevice:
def __init__(self, **args):
self.apps = []
self._logger = mock.MagicMock()
self._have_su = True
self._have_android_su = True
def clear_logcat(self, *args, **kwargs):
return True
def shell_output(self, *args, **kwargs):
return "A Fake Device"
def shell_bool(self, *args, **kwargs):
return True
def uninstall_app(self, apk_name):
return True
@ -41,6 +54,57 @@ def test_android():
android(metadata)
@mock.patch("mozperftest.system.android.ADBLoggedDevice")
def test_android_perf_tuning_rooted(device):
# Check to make sure that performance tuning runs
# on rooted devices correctly
device._have_su = True
device._have_android_su = True
with mock.patch(
"mozperftest.system.android_perf_tuner.PerformanceTuner.set_kernel_performance_parameters"
) as mockfunc:
tuner = PerformanceTuner(device)
tuner.tune_performance()
mockfunc.assert_called()
@mock.patch("mozperftest.system.android.ADBLoggedDevice")
def test_android_perf_tuning_nonrooted(device):
# Check to make sure that performance tuning runs
# on non-rooted devices correctly
device._have_su = False
device._have_android_su = False
with mock.patch(
"mozperftest.system.android_perf_tuner.PerformanceTuner.set_kernel_performance_parameters"
) as mockfunc:
tuner = PerformanceTuner(device)
tuner.tune_performance()
mockfunc.assert_not_called()
@mock.patch("mozperftest.system.android_perf_tuner.PerformanceTuner")
@mock.patch("mozperftest.system.android.ADBLoggedDevice")
def test_android_with_perftuning(device, tuner):
args = {
"flavor": "mobile-browser",
"android-install-apk": ["this.apk"],
"android": True,
"android-timeout": 30,
"android-capture-adb": "stdout",
"android-app-name": "org.mozilla.fenix",
"android-perf-tuning": True,
}
tuner.return_value = tuner
mach_cmd, metadata, env = get_running_env(**args)
system = env.layers[SYSTEM]
with system as android, silence(system):
android(metadata)
# Make sure the tuner was actually called
tuner.tune_performance.assert_called()
def test_android_failure():
# no patching so it'll try for real and fail
args = {

Просмотреть файл

@ -77,12 +77,14 @@ jobs:
--android-activity org.mozilla.fenix.IntentReceiverActivity
--android-clear-logcat
--android-capture-logcat logcat
--android-perf-tuning
--perfherder-metrics processLaunchToNavStart
--android-install-apk fenix_nightlysim_multicommit_armeabi_v7a
--hooks testing/performance/hooks_applink.py
--perfherder
--perfherder-app fenix
--browsertime-geckodriver ${MOZ_FETCHES_DIR}/geckodriver
--browsertime-iterations 10
--output $MOZ_FETCHES_DIR/../artifacts
--verbose
testing/performance/perftest_applink.js
@ -108,10 +110,12 @@ jobs:
--android-activity org.mozilla.fenix.IntentReceiverActivity
--android-clear-logcat
--android-capture-logcat logcat
--android-perf-tuning
--hooks testing/performance/hooks_applink.py
--perfherder
--perfherder-app fenix
--browsertime-geckodriver ${MOZ_FETCHES_DIR}/geckodriver
--browsertime-iterations 10
--output $MOZ_FETCHES_DIR/../artifacts
--verbose
testing/performance/perftest_applink.js
@ -135,12 +139,14 @@ jobs:
--android-activity org.mozilla.geckoview_example.GeckoViewActivity
--android-clear-logcat
--android-capture-logcat logcat
--android-perf-tuning
--perfherder-metrics processLaunchToNavStart
--android-install-apk gve_nightly_api16
--hooks testing/performance/hooks_applink.py
--perfherder
--perfherder-app geckoview
--browsertime-geckodriver ${MOZ_FETCHES_DIR}/geckodriver
--browsertime-iterations 10
--output $MOZ_FETCHES_DIR/../artifacts
--verbose
testing/performance/perftest_applink.js
@ -164,12 +170,14 @@ jobs:
--android-capture-logcat logcat
--android-app-name org.mozilla.geckoview_example
--android-activity org.mozilla.geckoview_example.GeckoViewActivity
--android-perf-tuning
--perfherder-metrics processLaunchToNavStart
--android-install-apk gve_nightly_api16
--hooks testing/performance/hooks_applink.py
--perfherder
--perfherder-app geckoview
--browsertime-geckodriver ${MOZ_FETCHES_DIR}/geckodriver
--browsertime-iterations 10
--output $MOZ_FETCHES_DIR/../artifacts
--verbose
testing/performance/perftest_applink.js