1cb93a386Sopenharmony_ci#!/usr/bin/python2
2cb93a386Sopenharmony_ci#
3cb93a386Sopenharmony_ci# Copyright 2019 Google Inc.
4cb93a386Sopenharmony_ci#
5cb93a386Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
6cb93a386Sopenharmony_ci# found in the LICENSE file.
7cb93a386Sopenharmony_ci#
8cb93a386Sopenharmony_ci# Helper script that takes as input 2 CSVs downloaded from perf.skia.org and
9cb93a386Sopenharmony_ci# outputs a CSV with test_name, avg_value1 (from CSV1), avg_value2 (from CSV2),
10cb93a386Sopenharmony_ci# perc_diff between avg_value1 and avg_value2.
11cb93a386Sopenharmony_ci# This script also discards NUM_OUTLIERS_TO_REMOVE min values and
12cb93a386Sopenharmony_ci# NUM_OUTLIERS_TO_REMOVE max values.
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ciimport csv
16cb93a386Sopenharmony_ciimport optparse
17cb93a386Sopenharmony_ciimport sys
18cb93a386Sopenharmony_ciimport re
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ciMISSING_STR = 'N/A'
22cb93a386Sopenharmony_ciNUM_OUTLIERS_TO_REMOVE = 2
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_cidef read_from_csv(csv_file):
26cb93a386Sopenharmony_ci  test_to_avg = {}
27cb93a386Sopenharmony_ci  with open(csv_file, 'rb') as f:
28cb93a386Sopenharmony_ci    csv_reader = csv.reader(f, delimiter=',')
29cb93a386Sopenharmony_ci    # First row should contain headers. Validate that it does.
30cb93a386Sopenharmony_ci    header_row = csv_reader.next()
31cb93a386Sopenharmony_ci    if header_row[0] != 'id':
32cb93a386Sopenharmony_ci      raise Exception('%s in unexpected format' % csv_file)
33cb93a386Sopenharmony_ci    p = re.compile('^.*,test=(.*),$')
34cb93a386Sopenharmony_ci    for v in csv_reader:
35cb93a386Sopenharmony_ci      # Extract the test name.
36cb93a386Sopenharmony_ci      result = p.search(v[0])
37cb93a386Sopenharmony_ci      test_name = result.group(1)
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ci      vals = [float(i) for i in v[1:]]
40cb93a386Sopenharmony_ci      vals.sort()
41cb93a386Sopenharmony_ci      # Discard outliers.
42cb93a386Sopenharmony_ci      vals = vals[NUM_OUTLIERS_TO_REMOVE:-NUM_OUTLIERS_TO_REMOVE]
43cb93a386Sopenharmony_ci      # Find the avg val.
44cb93a386Sopenharmony_ci      avg_val = reduce(lambda x, y: x+y, vals) / float(len(vals))
45cb93a386Sopenharmony_ci      test_to_avg[test_name] = avg_val
46cb93a386Sopenharmony_ci  return test_to_avg
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_cidef combine_results(d1, d2):
50cb93a386Sopenharmony_ci  test_to_result = {}
51cb93a386Sopenharmony_ci  for test1, v1 in d1.items():
52cb93a386Sopenharmony_ci    v2 = d2.get(test1, MISSING_STR)
53cb93a386Sopenharmony_ci    perc_diff = MISSING_STR
54cb93a386Sopenharmony_ci    if v2 != MISSING_STR:
55cb93a386Sopenharmony_ci      diff = v2 - v1
56cb93a386Sopenharmony_ci      avg = (v2 + v1)/2
57cb93a386Sopenharmony_ci      perc_diff = 0 if avg == 0 else diff/avg * 100
58cb93a386Sopenharmony_ci    result = {
59cb93a386Sopenharmony_ci        'test_name': test1,
60cb93a386Sopenharmony_ci        'csv1': v1,
61cb93a386Sopenharmony_ci        'csv2': v2,
62cb93a386Sopenharmony_ci        'perc_diff': perc_diff,
63cb93a386Sopenharmony_ci    }
64cb93a386Sopenharmony_ci    test_to_result[test1] = result
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci  # Also add keys in d2 and not d1.
67cb93a386Sopenharmony_ci  for test2, v2 in d2.items():
68cb93a386Sopenharmony_ci    if test2 in test_to_result:
69cb93a386Sopenharmony_ci      continue
70cb93a386Sopenharmony_ci    test_to_result[test2] = {
71cb93a386Sopenharmony_ci      'test_name': test2,
72cb93a386Sopenharmony_ci      'csv1': MISSING_STR,
73cb93a386Sopenharmony_ci      'csv2': v2,
74cb93a386Sopenharmony_ci      'perc_diff': MISSING_STR,
75cb93a386Sopenharmony_ci    }
76cb93a386Sopenharmony_ci
77cb93a386Sopenharmony_ci  return test_to_result
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_cidef write_to_csv(output_dict, output_csv):
81cb93a386Sopenharmony_ci  with open(output_csv, 'w') as f:
82cb93a386Sopenharmony_ci    fieldnames = ['test_name', 'csv1', 'csv2', 'perc_diff']
83cb93a386Sopenharmony_ci    writer = csv.DictWriter(f, fieldnames=fieldnames)
84cb93a386Sopenharmony_ci    writer.writeheader()
85cb93a386Sopenharmony_ci    tests = output_dict.keys()
86cb93a386Sopenharmony_ci    tests.sort()
87cb93a386Sopenharmony_ci    for test in tests:
88cb93a386Sopenharmony_ci      writer.writerow(output_dict[test])
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_cidef parse_and_output(csv1, csv2, output_csv):
92cb93a386Sopenharmony_ci  test_to_avg1 = read_from_csv(csv1)
93cb93a386Sopenharmony_ci  test_to_avg2 = read_from_csv(csv2)
94cb93a386Sopenharmony_ci  output_dict = combine_results(test_to_avg1, test_to_avg2)
95cb93a386Sopenharmony_ci  write_to_csv(output_dict, output_csv)
96cb93a386Sopenharmony_ci
97cb93a386Sopenharmony_ci
98cb93a386Sopenharmony_cidef main():
99cb93a386Sopenharmony_ci  option_parser = optparse.OptionParser()
100cb93a386Sopenharmony_ci  option_parser.add_option(
101cb93a386Sopenharmony_ci      '', '--csv1', type=str,
102cb93a386Sopenharmony_ci      help='The first CSV to parse.')
103cb93a386Sopenharmony_ci  option_parser.add_option(
104cb93a386Sopenharmony_ci      '', '--csv2', type=str,
105cb93a386Sopenharmony_ci      help='The second CSV to parse.')
106cb93a386Sopenharmony_ci  option_parser.add_option(
107cb93a386Sopenharmony_ci      '', '--output_csv', type=str,
108cb93a386Sopenharmony_ci      help='The file to write the output CSV to.')
109cb93a386Sopenharmony_ci  options, _ = option_parser.parse_args()
110cb93a386Sopenharmony_ci  sys.exit(parse_and_output(options.csv1, options.csv2, options.output_csv))
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_ci
113cb93a386Sopenharmony_ciif __name__ == '__main__':
114cb93a386Sopenharmony_ci  main()
115