In [1]:
import logging
reload(logging)
log_fmt = '%(asctime)-9s %(levelname)-8s: %(message)s'
logging.basicConfig(format=log_fmt)
# Change to info once the notebook runs ok
logging.getLogger().setLevel(logging.INFO)
In [2]:
%pylab inline
import collections
import copy
import json
import os
import pexpect as pe
from time import sleep
# Support to access the remote target
import devlib
from env import TestEnv
# from devlib.utils.android import adb_command
# Import support for Android devices
from android import Screen, Workload
# Support for trace events analysis
from trace import Trace
# Suport for FTrace events parsing and visualization
import trappy
import datetime
Devlib requires the ANDROID_HOME environment variable configured to point to your local installation of the Android SDK. If you have not this variable configured in the shell used to start the notebook server, you need to run the next cell to define where your Android SDK is installed.
In [3]:
# Setup Androd SDK
os.environ['ANDROID_HOME'] = '/home/eas/Work/Android/android-sdk-linux/'
# Setup Catapult for Systrace usage
CATAPULT_HOME = "/home/eas/Work/Android/catapult"
In [4]:
# Android device to target
DEVICE = 'GA0113TP0178'
# Ensure ADB is running as root
!adb -s {DEVICE} root
In case more than one Android device are conencted to the host, you must specify the ID of the device you want to target in my_target_conf
. Run adb devices
on your host to get the ID.
In [5]:
# Setup target configuration
my_conf = {
# Target platform and board
"platform" : 'android',
"device" : DEVICE,
# "emeter" : {
# "instrument" : "aep",
# "conf" : {
# 'labels' : ['BAT'],
# 'resistor_values' : [0.099],
# 'device_entry' : '/dev/ttyACM1',
# }
# },
# Folder where all the results will be collected
"results_dir" : "Android_Workloads",
# Define devlib modules to load
"modules" : [
'cpufreq' # enable CPUFreq support
],
# FTrace events to collect for all the tests configuration which have
# the "ftrace" flag enabled
"ftrace" : {
"events" : [
"sched_switch",
"sched_overutilized",
"sched_contrib_scale_f",
"sched_load_avg_cpu",
"sched_load_avg_task",
"sched_tune_tasks_update",
"sched_boost_cpu",
"sched_boost_task",
"sched_energy_diff",
"cpu_frequency",
"cpu_idle",
"cpu_capacity",
],
"buffsize" : 10 * 1024,
},
# Tools required by the experiments
"tools" : [ 'trace-cmd' ],
}
In [6]:
# List of configurations to test (keys of 'confs' defined in cell #9)
test_confs = ['std']
# List of workloads to run, each workload consists of a workload name
# followed by a list of workload specific parameters
test_wloads = [
# YouTube workload:
# Params:
# - video URL (with optional start time)
# - duration [s] to playback
'YouTube https://youtu.be/XSGBVzeBUbk?t=45s 15',
# Jankbench workload:
# Params:
# - id of the benchmakr to run
'Jankbench list_view',
# 'Jankbench image_list_view',
# 'Jankbench shadow_grid',
# 'Jankbench low_hitrate_text',
# 'Jankbench high_hitrate_text',
# 'Jankbench edit_text',
]
# Iterations for each test
iterations = 1
In [7]:
# Define what we want to collect as a list of strings.
# Supported values are
# energy - Use the my_conf's defined emeter to measure energy consumption across experiments
# ftrace - Collect an execution trace using trace-cmd
# systrace - Collect an execution trace using Systrace/Atrace
# NOTE: energy is automatically enabled in case an "emeter" configuration is defined in my_conf
collect = ''
This set of support functions will help us running the benchmark using different CPUFreq governors.
In [8]:
def set_performance():
target.cpufreq.set_all_governors('performance')
def set_powersave():
target.cpufreq.set_all_governors('powersave')
def set_interactive():
target.cpufreq.set_all_governors('interactive')
def set_sched():
target.cpufreq.set_all_governors('sched')
def set_ondemand():
target.cpufreq.set_all_governors('ondemand')
for cpu in target.list_online_cpus():
tunables = target.cpufreq.get_governor_tunables(cpu)
target.cpufreq.set_governor_tunables(
cpu,
'ondemand',
**{'sampling_rate' : tunables['sampling_rate_min']}
)
In [9]:
# Available test configurations
confs = {
'std' : {
'label' : 'int',
'set' : set_interactive,
},
'eas' : {
'label' : 'sch',
'set' : set_sched,
}
}
In [10]:
SYSTRACE_CMD = CATAPULT_HOME + "/systrace/systrace/systrace.py -e {} -o {} gfx view sched freq idle -t {}"
def experiment(wl, res_dir, conf_name, wload_name, iterations, collect=''):
# Load workload params
wload_kind = wload_name.split()[0]
wload_tag = wload_name.split()[1]\
.replace('https://youtu.be/', '')\
.replace('?t=', '_')
# Check for workload being available
wload = Workload.get(te, wload_kind)
if not wload:
return {}
# Setup test results folder
exp_dir = os.path.join(res_dir, conf_name, "{}_{}".format(wload_kind, wload_tag))
os.system('mkdir -p {}'.format(exp_dir));
# Configure governor
confs[conf_name]['set']()
# Configure screen to max brightness and no dimming
Screen.set_brightness(target, percent=100)
Screen.set_dim(target, auto=False)
Screen.set_timeout(target, 60*60*10) # 10 hours should be enought for an experiment
# Start the required tracing command
if 'ftrace' in collect:
# Start FTrace and Energy monitoring
te.ftrace.start()
elif 'systrace' in collect:
# Start systrace
trace_file = os.path.join(exp_dir, 'trace.html')
trace_cmd = SYSTRACE_CMD.format(DEVICE, trace_file, wload['duration'] * iterations)
logging.info('SysTrace: %s', trace_cmd)
systrace_output = pe.spawn(trace_cmd)
###########################
# Run the required workload
# Jankbench
if 'Jankbench' in wload_name:
db_file, nrg_data, nrg_file = wload.run(exp_dir, wload_tag, iterations, collect)
# YouTube
elif 'YouTube' in wload_name:
video_url = wload_name.split()[1]
video_duration_s = wload_name.split()[2]
db_file, nrg_data, nrg_file = wload.run(exp_dir, video_url, int(video_duration_s), collect)
###########################
# Stop the required trace command
if 'ftrace' in collect:
te.ftrace.stop()
# Collect and keep track of the trace
trace_file = os.path.join(exp_dir, 'trace.dat')
te.ftrace.get_trace(trace_file)
elif 'systrace' in collect:
logging.info('Waiting systrace report [%s]...', trace_file)
systrace_output.wait()
# Reset screen brightness and auto dimming
Screen.set_defaults(target, )
# Dump platform descriptor
te.platform_dump(exp_dir)
# return all the experiment data
if 'trace' in collect:
return {
'dir' : exp_dir,
'db_file' : db_file,
'nrg_data' : copy.deepcopy(nrg_data),
'nrg_file' : nrg_file,
'trace_file' : trace_file,
}
else:
return {
'dir' : exp_dir,
'db_file' : db_file,
'nrg_data' : copy.deepcopy(nrg_data),
'nrg_file' : nrg_file,
}
In [11]:
# # Cleanup Caiman energy meter temporary folders
# !rm -rf /tmp/eprobe-caiman-*
# # Ensure there are not other "caiman" instanced running for the specified device
# # my_conf['emeter']['conf']['device_entry']
# !killall caiman
In [12]:
# Initialize a test environment using:
te = TestEnv(my_conf, wipe=False)
target = te.target
In [13]:
# Unlock device screen (assume no password required)
target.execute('input keyevent 82')
# Intialize Workloads for this test environment
wl = Workload(te)
# The set of results for each comparison test
results = collections.defaultdict(dict)
# Enable energy collection if an emeter has been configured
if 'emeter' in my_conf and te.emeter:
logging.info('Enabling ENERGY collection')
collect += ' energy'
# Run the benchmark in all the configured governors
for conf_name in test_confs:
for idx,wload_name in enumerate(test_wloads):
wload_kind = wload_name.split()[0]
logging.info('------------------------')
logging.info('Test %d/%d: %s in %s configuration',
idx+1, len(test_wloads), wload_kind.upper(), conf_name.upper())
res = experiment(wl, te.res_dir, conf_name, wload_name, iterations, collect)
results[conf_name][wload_name] = res
# Save collected results
res_file = os.path.join(te.res_dir, conf_name, 'results.json')
with open(res_file, 'w') as fh:
json.dump(results[conf_name], fh, indent=4)
In [16]:
for conf_name in test_confs:
for idx,wload_name in enumerate(test_wloads):
nrg = 'NaN'
if results[conf_name][wload_name]['nrg_data']:
nrg = '{:6.1f}'.format(float(results[conf_name][wload_name]['nrg_data']['BAT']))
print "Energy consumption {}, {:52}: {}".format(conf_name.upper(), wload_name.upper(), nrg)