1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from collections import defaultdict 6 7from telemetry.value import failure 8from telemetry.value import merge_values 9from telemetry.value import skip 10 11 12class Summary(object): 13 """Computes summary values from the per-page-run values produced by a test. 14 15 Some telemetry benchmark repeat a number of times in order to get a reliable 16 measurement. The test does not have to handle merging of these runs: 17 summarizer does it for you. 18 19 For instance, if two pages run, 3 and 1 time respectively: 20 ScalarValue(page1, 'foo', units='ms', 1) 21 ScalarValue(page1, 'foo', units='ms', 1) 22 ScalarValue(page1, 'foo', units='ms', 1) 23 ScalarValue(page2, 'foo', units='ms', 2) 24 25 Then summarizer will produce two sets of values. First, 26 computed_per_page_values: 27 [ 28 ListOfScalarValues(page1, 'foo', units='ms', [1,1,1])], 29 ListOfScalarValues(page2, 'foo', units='ms', [2])] 30 ] 31 32 In addition, it will produce a summary value: 33 [ 34 ListOfScalarValues(page=None, 'foo', units='ms', [1,1,1,2])] 35 ] 36 37 """ 38 def __init__(self, all_page_specific_values, 39 key_func=merge_values.DefaultKeyFunc): 40 had_failures = any(isinstance(v, failure.FailureValue) for v in 41 all_page_specific_values) 42 self.had_failures = had_failures 43 self._computed_per_page_values = [] 44 self._computed_summary_values = [] 45 self._interleaved_computed_per_page_values_and_summaries = [] 46 self._key_func = key_func 47 self._ComputePerPageValues(all_page_specific_values) 48 49 @property 50 def computed_per_page_values(self): 51 return self._computed_per_page_values 52 53 @property 54 def computed_summary_values(self): 55 return self._computed_summary_values 56 57 @property 58 def interleaved_computed_per_page_values_and_summaries(self): 59 """Returns the computed per page values and summary values interleaved. 60 61 All the results for a given name are printed together. First per page 62 values, then summary values. 63 64 """ 65 return self._interleaved_computed_per_page_values_and_summaries 66 67 def _ComputePerPageValues(self, all_page_specific_values): 68 all_successful_page_values = [ 69 v for v in all_page_specific_values if not (isinstance( 70 v, failure.FailureValue) or isinstance(v, skip.SkipValue))] 71 72 # We will later need to determine how many values were originally created 73 # for each value name, to apply a workaround meant to clean up the printf 74 # output. 75 num_successful_pages_for_key = defaultdict(int) 76 for v in all_successful_page_values: 77 num_successful_pages_for_key[self._key_func(v)] += 1 78 79 # By here, due to page repeat options, all_values_from_successful_pages 80 # contains values of the same name not only from mulitple pages, but also 81 # from the same name. So even if, for instance, only one page ran, it may 82 # have run twice, producing two 'x' values. 83 # 84 # So, get rid of the repeated pages by merging. 85 merged_page_values = merge_values.MergeLikeValuesFromSamePage( 86 all_successful_page_values, self._key_func) 87 88 # Now we have a bunch of values, but there is only one value_name per page. 89 # Suppose page1 and page2 ran, producing values x and y. We want to print 90 # x for page1 91 # x for page2 92 # x for page1, page2 combined 93 # 94 # y for page1 95 # y for page2 96 # y for page1, page2 combined 97 # 98 # We already have the x values in the values array. But, we will need 99 # them indexable by summary key. 100 # 101 # The following dict maps summary_key -> list of pages that have values of 102 # that name. 103 per_page_values_by_key = defaultdict(list) 104 for value in merged_page_values: 105 per_page_values_by_key[self._key_func(value)].append(value) 106 107 # We already have the x values in the values array. But, we also need 108 # the values merged across the pages. And, we will need them indexed by 109 # summary key so that we can find them when printing out value names in 110 # alphabetical order. 111 merged_pages_value_by_key = {} 112 if not self.had_failures: 113 for value in merge_values.MergeLikeValuesFromDifferentPages( 114 all_successful_page_values, self._key_func): 115 value_key = self._key_func(value) 116 assert value_key not in merged_pages_value_by_key 117 merged_pages_value_by_key[value_key] = value 118 119 keys = sorted(set([self._key_func(v) for v in merged_page_values])) 120 121 # Time to walk through the values by key, printing first the page-specific 122 # values and then the merged_site value. 123 for key in keys: 124 per_page_values = per_page_values_by_key.get(key, []) 125 126 # Sort the values by their URL. 127 sorted_per_page_values = list(per_page_values) 128 sorted_per_page_values.sort( 129 key=lambda per_page_values: per_page_values.page.display_name) 130 131 # Output the page-specific results. 132 num_successful_pages_for_this_key = ( 133 num_successful_pages_for_key[key]) 134 for per_page_value in sorted_per_page_values: 135 self._ComputePerPageValue(per_page_value, 136 num_successful_pages_for_this_key) 137 138 # Output the combined values. 139 merged_pages_value = merged_pages_value_by_key.get(key, None) 140 if merged_pages_value: 141 self._computed_summary_values.append(merged_pages_value) 142 self._interleaved_computed_per_page_values_and_summaries.append( 143 merged_pages_value) 144 145 def _ComputePerPageValue( 146 self, value, num_successful_pages_for_this_value_name): 147 if num_successful_pages_for_this_value_name >= 1: 148 # Save the result. 149 self._computed_per_page_values.append(value) 150 self._interleaved_computed_per_page_values_and_summaries.append(value) 151