Source code

Revision control

Other Tools

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import absolute_import, division
import math
import six
"""
data filters:
takes a series of run data and applies statistical transforms to it
Each filter is a simple function, but it also have attached a special
`prepare` method that create a tuple with one instance of a
:class:`Filter`; this allow to write stuff like::
from talos import filter
filters = filter.ignore_first.prepare(1) + filter.median.prepare()
for filter in filters:
data = filter(data)
# data is filtered
"""
_FILTERS = {}
class Filter(object):
def __init__(self, func, *args, **kwargs):
"""
Takes a filter function, and save args and kwargs that
should be used when the filter is used.
"""
self.func = func
self.args = args
self.kwargs = kwargs
def apply(self, data):
"""
Apply the filter on the data, and return the new data
"""
return self.func(data, *self.args, **self.kwargs)
def define_filter(func):
"""
decorator to attach the prepare method.
"""
def prepare(*args, **kwargs):
return (Filter(func, *args, **kwargs),)
func.prepare = prepare
return func
def register_filter(func):
"""
all filters defined in this module
should be registered
"""
global _FILTERS
_FILTERS[func.__name__] = func
return func
def filters(*args):
global _FILTERS
filters_ = [_FILTERS[filter] for filter in args]
return filters_
def apply(data, filters):
for filter in filters:
data = filter(data)
return data
def parse(string_):
def to_number(string_number):
try:
return int(string_number)
except ValueError:
return float(string_number)
tokens = string_.split(":")
func = tokens[0]
digits = []
if len(tokens) > 1:
digits.extend(tokens[1].split(","))
digits = [to_number(digit) for digit in digits]
return [func, digits]
# filters that return a scalar
@register_filter
@define_filter
def mean(series):
"""
mean of data; needs at least one data point
"""
return sum(series) / float(len(series))
@register_filter
@define_filter
def median(series):
"""
median of data; needs at least one data point
"""
series = sorted(series)
if len(series) % 2:
# odd
return series[int(len(series) / 2)]
else:
# even
middle = int(len(series) / 2) # the higher of the middle 2, actually
return 0.5 * (series[middle - 1] + series[middle])
@register_filter
@define_filter
def variance(series):
"""
"""
_mean = mean(series)
variance = sum([(i - _mean) ** 2 for i in series]) / float(len(series))
return variance
@register_filter
@define_filter
def stddev(series):
"""
"""
return variance(series) ** 0.5
@register_filter
@define_filter
def dromaeo(series):
"""
dromaeo: https://wiki.mozilla.org/Dromaeo, pull the internal calculation
out
* This is for 'runs/s' based tests, not 'ms' tests.
* chunksize: defined in dromaeo: tests/dromaeo/webrunner.js#l8
"""
means = []
chunksize = 5
series = list(dromaeo_chunks(series, chunksize))
for i in series:
means.append(mean(i))
return geometric_mean(means)
@register_filter
@define_filter
def dromaeo_chunks(series, size):
for i in six.moves.range(0, len(series), size):
yield series[i : i + size]
@register_filter
@define_filter
def geometric_mean(series):
"""
"""
total = 0
for i in series:
total += math.log(i + 1)
# pylint --py3k W1619
return math.exp(total / len(series)) - 1
# filters that return a list
@register_filter
@define_filter
def ignore_first(series, number=1):
"""
ignore first datapoint
"""
if len(series) <= number:
# don't modify short series
return series
return series[number:]
@register_filter
@define_filter
def ignore(series, function):
"""
ignore the first value of a list given by function
"""
if len(series) <= 1:
# don't modify short series
return series
series = series[:] # do not mutate the original series
value = function(series)
series.remove(value)
return series
@register_filter
@define_filter
def ignore_max(series):
"""
ignore maximum data point
"""
return ignore(series, max)
@register_filter
@define_filter
def ignore_min(series):
"""
ignore minimum data point
"""
return ignore(series, min)
@register_filter
@define_filter
def v8_subtest(series, name):
"""
v8 benchmark score - modified for no sub benchmarks.
* removed Crypto and kept Encrypt/Decrypt standalone
* removed EarlyBoyer and kept Earley/Boyer standalone
this is not 100% in parity but within .3%
"""
reference = {
"Encrypt": 266181.0,
"Decrypt": 266181.0,
"DeltaBlue": 66118.0,
"Earley": 666463.0,
"Boyer": 666463.0,
"NavierStokes": 1484000.0,
"RayTrace": 739989.0,
"RegExp": 910985.0,
"Richards": 35302.0,
"Splay": 81491.0,
}
# pylint --py3k W1619
return reference[name] / geometric_mean(series)
@register_filter
@define_filter
def responsiveness_Metric(val_list):
return sum([float(x) * float(x) / 1000000.0 for x in val_list])