1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5# originally taken from /testing/talos/talos/filter.py 6 7from __future__ import absolute_import, division 8 9import math 10 11""" 12data filters: 13takes a series of run data and applies statistical transforms to it 14 15Each filter is a simple function, but it also have attached a special 16`prepare` method that create a tuple with one instance of a 17:class:`Filter`; this allow to write stuff like:: 18 19 from raptor import filters 20 filter_list = filters.ignore_first.prepare(1) + filters.median.prepare() 21 22 for filter in filter_list: 23 data = filter(data) 24 # data is filtered 25""" 26 27_FILTERS = {} 28 29 30class Filter(object): 31 def __init__(self, func, *args, **kwargs): 32 """ 33 Takes a filter function, and save args and kwargs that 34 should be used when the filter is used. 35 """ 36 self.func = func 37 self.args = args 38 self.kwargs = kwargs 39 40 def apply(self, data): 41 """ 42 Apply the filter on the data, and return the new data 43 """ 44 return self.func(data, *self.args, **self.kwargs) 45 46 47def define_filter(func): 48 """ 49 decorator to attach the prepare method. 50 """ 51 52 def prepare(*args, **kwargs): 53 return (Filter(func, *args, **kwargs),) 54 55 func.prepare = prepare 56 return func 57 58 59def register_filter(func): 60 """ 61 all filters defined in this module 62 should be registered 63 """ 64 global _FILTERS 65 66 _FILTERS[func.__name__] = func 67 return func 68 69 70def filters(*args): 71 global _FILTERS 72 73 filters_ = [_FILTERS[filter] for filter in args] 74 return filters_ 75 76 77def apply(data, filters): 78 for filter in filters: 79 data = filter(data) 80 81 return data 82 83 84def parse(string_): 85 def to_number(string_number): 86 try: 87 return int(string_number) 88 except ValueError: 89 return float(string_number) 90 91 tokens = string_.split(":") 92 93 func = tokens[0] 94 digits = [] 95 if len(tokens) > 1: 96 digits.extend(tokens[1].split(",")) 97 digits = [to_number(digit) for digit in digits] 98 99 return [func, digits] 100 101 102# filters that return a scalar 103 104 105@register_filter 106@define_filter 107def mean(series): 108 """ 109 mean of data; needs at least one data point 110 """ 111 return sum(series) / float(len(series)) 112 113 114@register_filter 115@define_filter 116def median(series): 117 """ 118 median of data; needs at least one data point 119 """ 120 series = sorted(series) 121 if len(series) % 2: 122 # odd 123 # pylint --py3k W1619 124 # must force to int to use as index. 125 return series[int(len(series) / 2)] 126 else: 127 # even 128 # pylint --py3k W1619 129 middle = int(len(series) / 2) # the higher of the middle 2, actually 130 return 0.5 * (series[middle - 1] + series[middle]) 131 132 133@register_filter 134@define_filter 135def variance(series): 136 """ 137 variance: http://en.wikipedia.org/wiki/Variance 138 """ 139 140 _mean = mean(series) 141 variance = sum([(i - _mean) ** 2 for i in series]) / float(len(series)) 142 return variance 143 144 145@register_filter 146@define_filter 147def stddev(series): 148 """ 149 standard deviation: http://en.wikipedia.org/wiki/Standard_deviation 150 """ 151 return variance(series) ** 0.5 152 153 154@register_filter 155@define_filter 156def dromaeo(series): 157 """ 158 dromaeo: https://wiki.mozilla.org/Dromaeo, pull the internal calculation 159 out 160 * This is for 'runs/s' based tests, not 'ms' tests. 161 * chunksize: defined in dromaeo: tests/dromaeo/webrunner.js#l8 162 """ 163 means = [] 164 chunksize = 5 165 series = list(dromaeo_chunks(series, chunksize)) 166 for i in series: 167 means.append(mean(i)) 168 return geometric_mean(means) 169 170 171@register_filter 172@define_filter 173def dromaeo_chunks(series, size): 174 for i in range(0, len(series), size): 175 yield series[i : i + size] 176 177 178@register_filter 179@define_filter 180def geometric_mean(series): 181 """ 182 geometric_mean: http://en.wikipedia.org/wiki/Geometric_mean 183 """ 184 total = 0 185 for i in series: 186 total += math.log(i + 1) 187 # pylint --py3k W1619 188 return math.exp(total / len(series)) - 1 189 190 191# filters that return a list 192 193 194@register_filter 195@define_filter 196def ignore_first(series, number=1): 197 """ 198 ignore first datapoint 199 """ 200 if len(series) <= number: 201 # don't modify short series 202 return series 203 return series[number:] 204 205 206@register_filter 207@define_filter 208def ignore(series, function): 209 """ 210 ignore the first value of a list given by function 211 """ 212 if len(series) <= 1: 213 # don't modify short series 214 return series 215 series = series[:] # do not mutate the original series 216 value = function(series) 217 series.remove(value) 218 return series 219 220 221@register_filter 222@define_filter 223def ignore_max(series): 224 """ 225 ignore maximum data point 226 """ 227 return ignore(series, max) 228 229 230@register_filter 231@define_filter 232def ignore_min(series): 233 """ 234 ignore minimum data point 235 """ 236 return ignore(series, min) 237 238 239@register_filter 240@define_filter 241def ignore_negative(series): 242 """ 243 ignore data points that have a negative value 244 caution: if all data values are < 0, this will return an empty list 245 """ 246 if len(series) <= 1: 247 # don't modify short series 248 return series 249 series = series[:] # do not mutate the original series 250 return list(filter(lambda x: x >= 0, series)) 251 252 253@register_filter 254@define_filter 255def v8_subtest(series, name): 256 """ 257 v8 benchmark score - modified for no sub benchmarks. 258 * removed Crypto and kept Encrypt/Decrypt standalone 259 * removed EarlyBoyer and kept Earley/Boyer standalone 260 261 this is not 100% in parity but within .3% 262 """ 263 reference = { 264 "Encrypt": 266181.0, 265 "Decrypt": 266181.0, 266 "DeltaBlue": 66118.0, 267 "Earley": 666463.0, 268 "Boyer": 666463.0, 269 "NavierStokes": 1484000.0, 270 "RayTrace": 739989.0, 271 "RegExp": 910985.0, 272 "Richards": 35302.0, 273 "Splay": 81491.0, 274 } 275 276 # pylint --py3k W1619 277 return reference[name] / geometric_mean(series) 278 279 280@register_filter 281@define_filter 282def responsiveness_Metric(val_list): 283 return sum([float(x) * float(x) / 1000000.0 for x in val_list]) 284