Most devices today refresh their screens 60 times a second. If there’s an animation or transition running, or the user is scrolling, applications need to match the device’s refresh rate and put up a new picture, or frame, for each of those screen refreshes. When one fails to meet this budget - 16.6 ms, the frame rate drops, and the content judders on screen. This is often referred to as jank, and it negatively impacts the user's experience.
This benchmark is used to count the jank frames for different types of activities: list and image list view fling, text render, text editing, etc. Also ftraces are captured during the benchmark run and represented at the end of the notebook.
In [1]:
from conf import LisaLogging
LisaLogging.setup()
In [2]:
%pylab inline
import json
import os
# Support to access the remote target
import devlib
from env import TestEnv
# 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 pandas as pd
import sqlite3
This function helps us run our experiments:
In [3]:
def experiment():
# Configure governor
target.cpufreq.set_all_governors('sched')
# Get workload
wload = Workload.getInstance(te, 'Jankbench')
# Run Jankbench workload
wload.run(te.res_dir, test_name='list_view', iterations=1, collect='ftrace')
# Dump platform descriptor
te.platform_dump(te.res_dir)
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 a cell to define where your Android SDK is installed or specify the ANDROID_HOME in your target configuration.
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 [4]:
# Setup target configuration
my_conf = {
# Target platform and board
"platform" : 'android',
"board" : 'pixel',
# Device
"device" : "FA6A10306347",
# Android home
"ANDROID_HOME" : "/home/vagrant/lisa/tools/android-sdk-linux/",
# Folder where all the results will be collected
"results_dir" : "Jankbench_example",
# 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_wakeup",
"sched_wakeup_new",
"sched_overutilized",
"sched_load_avg_cpu",
"sched_load_avg_task",
"cpu_capacity",
"cpu_frequency",
],
"buffsize" : 100 * 1024,
},
# Tools required by the experiments
"tools" : [ 'trace-cmd', 'taskset'],
}
In [5]:
# Initialize a test environment using:
te = TestEnv(my_conf, wipe=False)
target = te.target
In [6]:
# Intialize Workloads for this test environment
results = experiment()
In [7]:
def import_db(path):
# Selection of columns of interest
COLS = ['_id', 'name', 'run_id', 'iteration', 'total_duration', 'jank_frame']
data = []
db = '{}/{}'.format(te.res_dir, 'BenchmarkResults')
conn = sqlite3.connect(db)
for row in conn.execute('SELECT {} FROM ui_results'.format(','.join(COLS))):
row = ('sched', ) + row
data.append(row)
print "DB[ {} ]: {:6d} rows imported".format('sched', len(data))
return pd.DataFrame(data, columns=['test', ] + COLS)
df = import_db(te.res_dir)
In [8]:
def overall_statistics(df):
byname_test = df.groupby(['name','test']).total_duration.describe(percentiles=[0.9, 0.95, 0.99])
stats = pd.DataFrame(byname_test)
# If using old Pandas, convert GroupBy.describe format to new version
# http://pandas.pydata.org/pandas-docs/version/0.20/whatsnew.html#groupby-describe-formatting
if 'count' not in stats.columns:
stats = stats.unstack
return stats
stats = overall_statistics(df)
stats
Out[8]:
In [9]:
def total_duration_plot(data):
fig, axes = plt.subplots(figsize=(16,8))
bp = data.boxplot(by=['name','test'], column='total_duration', ax=axes, return_type='dict')
fig.suptitle('')
xlabels = [item.get_text() for item in axes.xaxis.get_ticklabels()]
axes.set_xticklabels(xlabels, rotation=90)
axes.set_ylim(0,20)
axes.set_ylabel('ms');
total_duration_plot(data = df)
In [10]:
def total_duration_cumulative_distribution(df):
fig, axes = plt.subplots()
plt.axhline(y=16, linewidth=2, color='r', linestyle='--')
colors = iter(cm.rainbow(np.linspace(0, 1, 1)))
df = pd.DataFrame(sorted(df.total_duration))
df.plot(
figsize=(16,8),
ax=axes,
color=next(colors),
linewidth=2,
ylim=(0,80),
legend=False);
plt.legend(('16ms',) + tuple(['sched']), loc='best');
total_duration_cumulative_distribution(df)
In [11]:
# Parse all traces
platform_file = os.path.join(te.res_dir, 'platform.json')
with open(platform_file, 'r') as fh:
platform = json.load(fh)
trace_file = os.path.join(te.res_dir, 'trace.dat')
trace = Trace(trace_file, my_conf['ftrace']['events'], platform)
trappy.plotter.plot_trace(trace.ftrace)
In [12]:
# Plot latency events for a specified task
latency_stats_df = trace.analysis.latency.plotLatency('droid.benchmark')
In [13]:
try:
trace.analysis.frequency.plotClusterFrequencies();
logging.info('Plotting cluster frequencies for [sched]...')
except: pass