Joel Fernandes | b5fbd41 | 2017-10-26 12:24:30 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | import optparse |
| 4 | import sys |
| 5 | import sqlite3 |
| 6 | import scipy.stats |
| 7 | import numpy |
| 8 | from math import log10, floor |
| 9 | import matplotlib |
| 10 | |
| 11 | matplotlib.use("Agg") |
| 12 | |
| 13 | import matplotlib.pyplot as plt |
| 14 | import pylab |
| 15 | |
| 16 | import adbutil |
| 17 | from devices import DEVICES |
| 18 | |
| 19 | DB_PATH="/data/data/com.android.benchmark/databases/BenchmarkResults" |
| 20 | OUT_PATH = "db/" |
| 21 | |
| 22 | QUERY_BAD_FRAME = ("select run_id, name, iteration, total_duration from ui_results " |
| 23 | "where total_duration >= 16 order by run_id, name, iteration") |
| 24 | QUERY_PERCENT_JANK = ("select run_id, name, iteration, sum(jank_frame) as jank_count, count (*) as total " |
| 25 | "from ui_results group by run_id, name, iteration") |
| 26 | |
| 27 | SKIP_TESTS = [ |
| 28 | # "BMUpload", |
| 29 | # "Low-hitrate text render", |
| 30 | # "High-hitrate text render", |
| 31 | # "Edit Text Input", |
| 32 | # "List View Fling" |
| 33 | ] |
| 34 | |
| 35 | INCLUDE_TESTS = [ |
| 36 | #"BMUpload" |
| 37 | #"Shadow Grid Fling" |
| 38 | #"Image List View Fling" |
| 39 | #"Edit Text Input" |
| 40 | ] |
| 41 | |
| 42 | class IterationResult: |
| 43 | def __init__(self): |
| 44 | self.durations = [] |
| 45 | self.jank_count = 0 |
| 46 | self.total_count = 0 |
| 47 | |
| 48 | |
| 49 | def get_scoremap(dbpath): |
| 50 | db = sqlite3.connect(dbpath) |
| 51 | rows = db.execute(QUERY_BAD_FRAME) |
| 52 | |
| 53 | scoremap = {} |
| 54 | for row in rows: |
| 55 | run_id = row[0] |
| 56 | name = row[1] |
| 57 | iteration = row[2] |
| 58 | total_duration = row[3] |
| 59 | |
| 60 | if not run_id in scoremap: |
| 61 | scoremap[run_id] = {} |
| 62 | |
| 63 | if not name in scoremap[run_id]: |
| 64 | scoremap[run_id][name] = {} |
| 65 | |
| 66 | if not iteration in scoremap[run_id][name]: |
| 67 | scoremap[run_id][name][iteration] = IterationResult() |
| 68 | |
| 69 | scoremap[run_id][name][iteration].durations.append(float(total_duration)) |
| 70 | |
| 71 | for row in db.execute(QUERY_PERCENT_JANK): |
| 72 | run_id = row[0] |
| 73 | name = row[1] |
| 74 | iteration = row[2] |
| 75 | jank_count = row[3] |
| 76 | total_count = row[4] |
| 77 | |
| 78 | if run_id in scoremap.keys() and name in scoremap[run_id].keys() and iteration in scoremap[run_id][name].keys(): |
| 79 | scoremap[run_id][name][iteration].jank_count = long(jank_count) |
| 80 | scoremap[run_id][name][iteration].total_count = long(total_count) |
| 81 | |
| 82 | db.close() |
| 83 | return scoremap |
| 84 | |
| 85 | def round_to_2(val): |
| 86 | return val |
| 87 | if val == 0: |
| 88 | return val |
| 89 | return round(val , -int(floor(log10(abs(val)))) + 1) |
| 90 | |
| 91 | def score_device(name, serial, pull = False, verbose = False): |
| 92 | dbpath = OUT_PATH + name + ".db" |
| 93 | |
| 94 | if pull: |
| 95 | adbutil.root(serial) |
| 96 | adbutil.pull(serial, DB_PATH, dbpath) |
| 97 | |
| 98 | scoremap = None |
| 99 | try: |
| 100 | scoremap = get_scoremap(dbpath) |
| 101 | except sqlite3.DatabaseError: |
| 102 | print "Database corrupt, fetching..." |
| 103 | adbutil.root(serial) |
| 104 | adbutil.pull(serial, DB_PATH, dbpath) |
| 105 | scoremap = get_scoremap(dbpath) |
| 106 | |
| 107 | per_test_score = {} |
| 108 | per_test_sample_count = {} |
| 109 | global_overall = {} |
| 110 | |
| 111 | for run_id in iter(scoremap): |
| 112 | overall = [] |
| 113 | if len(scoremap[run_id]) < 1: |
| 114 | if verbose: |
| 115 | print "Skipping short run %s" % run_id |
| 116 | continue |
| 117 | print "Run: %s" % run_id |
| 118 | for test in iter(scoremap[run_id]): |
| 119 | if test in SKIP_TESTS: |
| 120 | continue |
| 121 | if INCLUDE_TESTS and test not in INCLUDE_TESTS: |
| 122 | continue |
| 123 | if verbose: |
| 124 | print "\t%s" % test |
| 125 | scores = [] |
| 126 | means = [] |
| 127 | stddevs = [] |
| 128 | pjs = [] |
| 129 | sample_count = 0 |
| 130 | hit_min_count = 0 |
| 131 | # try pooling together all iterations |
| 132 | for iteration in iter(scoremap[run_id][test]): |
| 133 | res = scoremap[run_id][test][iteration] |
| 134 | stddev = round_to_2(numpy.std(res.durations)) |
| 135 | mean = round_to_2(numpy.mean(res.durations)) |
| 136 | sample_count += len(res.durations) |
| 137 | pj = round_to_2(100 * res.jank_count / float(res.total_count)) |
| 138 | score = stddev * mean * pj |
| 139 | score = 100 * len(res.durations) / float(res.total_count) |
| 140 | if score == 0: |
| 141 | score = 1 |
| 142 | scores.append(score) |
| 143 | means.append(mean) |
| 144 | stddevs.append(stddev) |
| 145 | pjs.append(pj) |
| 146 | if verbose: |
| 147 | print "\t%s: Score = %f x %f x %f = %f (%d samples)" % (iteration, stddev, mean, pj, score, len(res.durations)) |
| 148 | |
| 149 | if verbose: |
| 150 | print "\tHit min: %d" % hit_min_count |
| 151 | print "\tMean Variation: %0.2f%%" % (100 * scipy.stats.variation(means)) |
| 152 | print "\tStdDev Variation: %0.2f%%" % (100 * scipy.stats.variation(stddevs)) |
| 153 | print "\tPJ Variation: %0.2f%%" % (100 * scipy.stats.variation(pjs)) |
| 154 | |
| 155 | geo_run = numpy.mean(scores) |
| 156 | if test not in per_test_score: |
| 157 | per_test_score[test] = [] |
| 158 | |
| 159 | if test not in per_test_sample_count: |
| 160 | per_test_sample_count[test] = [] |
| 161 | |
| 162 | sample_count /= len(scoremap[run_id][test]) |
| 163 | |
| 164 | per_test_score[test].append(geo_run) |
| 165 | per_test_sample_count[test].append(int(sample_count)) |
| 166 | overall.append(geo_run) |
| 167 | |
| 168 | if not verbose: |
| 169 | print "\t%s:\t%0.2f (%0.2f avg. sample count)" % (test, geo_run, sample_count) |
| 170 | else: |
| 171 | print "\tOverall:\t%0.2f (%0.2f avg. sample count)" % (geo_run, sample_count) |
| 172 | print "" |
| 173 | |
| 174 | global_overall[run_id] = scipy.stats.gmean(overall) |
| 175 | print "Run Overall: %f" % global_overall[run_id] |
| 176 | print "" |
| 177 | |
| 178 | print "" |
| 179 | print "Variability (CV) - %s:" % name |
| 180 | |
| 181 | worst_offender_test = None |
| 182 | worst_offender_variation = 0 |
| 183 | for test in per_test_score: |
| 184 | variation = 100 * scipy.stats.variation(per_test_score[test]) |
| 185 | if worst_offender_variation < variation: |
| 186 | worst_offender_test = test |
| 187 | worst_offender_variation = variation |
| 188 | print "\t%s:\t%0.2f%% (%0.2f avg sample count)" % (test, variation, numpy.mean(per_test_sample_count[test])) |
| 189 | |
| 190 | print "\tOverall: %0.2f%%" % (100 * scipy.stats.variation([x for x in global_overall.values()])) |
| 191 | print "" |
| 192 | |
| 193 | return { |
| 194 | "overall": global_overall.values(), |
| 195 | "worst_offender_test": (name, worst_offender_test, worst_offender_variation) |
| 196 | } |
| 197 | |
| 198 | def parse_options(argv): |
| 199 | usage = 'Usage: %prog [options]' |
| 200 | desc = 'Example: %prog' |
| 201 | parser = optparse.OptionParser(usage=usage, description=desc) |
| 202 | parser.add_option("-p", dest='pull', action="store_true") |
| 203 | parser.add_option("-d", dest='device', action="store") |
| 204 | parser.add_option("-v", dest='verbose', action="store_true") |
| 205 | options, categories = parser.parse_args(argv[1:]) |
| 206 | return options |
| 207 | |
| 208 | def main(): |
| 209 | options = parse_options(sys.argv) |
| 210 | if options.device != None: |
| 211 | score_device(options.device, DEVICES[options.device], options.pull, options.verbose) |
| 212 | else: |
| 213 | device_scores = [] |
| 214 | worst_offenders = [] |
| 215 | for name, serial in DEVICES.iteritems(): |
| 216 | print "======== %s =========" % name |
| 217 | result = score_device(name, serial, options.pull, options.verbose) |
| 218 | device_scores.append((name, result["overall"])) |
| 219 | worst_offenders.append(result["worst_offender_test"]) |
| 220 | |
| 221 | |
| 222 | device_scores.sort(cmp=(lambda x, y: cmp(x[1], y[1]))) |
| 223 | print "Ranking by max overall score:" |
| 224 | for name, score in device_scores: |
| 225 | plt.plot([0, 1, 2, 3, 4, 5], score, label=name) |
| 226 | print "\t%s: %s" % (name, score) |
| 227 | |
| 228 | plt.ylabel("Jank %") |
| 229 | plt.xlabel("Iteration") |
| 230 | plt.title("Jank Percentage") |
| 231 | plt.legend() |
| 232 | pylab.savefig("holy.png", bbox_inches="tight") |
| 233 | |
| 234 | print "Worst offender tests:" |
| 235 | for device, test, variation in worst_offenders: |
| 236 | print "\t%s: %s %.2f%%" % (device, test, variation) |
| 237 | |
| 238 | if __name__ == "__main__": |
| 239 | main() |
| 240 | |