| Index: tools/perf/metrics/discrepancy.py
|
| diff --git a/tools/perf/metrics/discrepancy.py b/tools/perf/metrics/discrepancy.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8ec3d2c85de9b3d4d3179769810e89211e0f2f97
|
| --- /dev/null
|
| +++ b/tools/perf/metrics/discrepancy.py
|
| @@ -0,0 +1,110 @@
|
| +# Copyright 2013 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +import bisect
|
| +import math
|
| +
|
| +def Clamp(value, low=0.0, high=1.0):
|
| + return min(max(value, low), high)
|
| +
|
| +def NormalizeSamples(samples):
|
| + ''' Sort the N samples, and map them linearly to the range [0,1] such that the
|
| + first sample is 0.5/N and the last sample is (N-0.5)/N. Background: the
|
| + discrepancy of the sample set i/(N-1); i=0,...,N-1 is 2/N, twice the
|
| + discrepancy of the sample set (i+1/2)/N; i=0,...,N-1. In our case we
|
| + don't want to distinguish between these two cases, as our original domain
|
| + is not bounded (it is for Monte Carlo integration, where discrepancy was
|
| + first used).
|
| + '''
|
| + samples = sorted(samples)
|
| + low = min(samples)
|
| + high = max(samples)
|
| + new_low = 0.5 / len(samples)
|
| + new_high = (len(samples)-0.5) / len(samples)
|
| + scale = (new_high - new_low) / (high - low)
|
| + for i in xrange(0, len(samples)):
|
| + samples[i] = float(samples[i] - low) * scale + new_low
|
| + return samples, scale
|
| +
|
| +def Discrepancy(samples, interval_multiplier = 10000):
|
| + ''' Compute the discrepancy of a set of 1D samples from the unit interval
|
| + [0,1]. The samples must be sorted.
|
| +
|
| + http://en.wikipedia.org/wiki/Low-discrepancy_sequence
|
| + http://mathworld.wolfram.com/Discrepancy.html
|
| + '''
|
| + if (len(samples) < 3):
|
| + return 0
|
| +
|
| + max_local_discrepancy = 0
|
| + locations = []
|
| + # For each location, stores the number of samples less than that location.
|
| + left = []
|
| + # For each location, stores the number of samples less than or equal to that
|
| + # location.
|
| + right = []
|
| +
|
| + interval_count = len(samples) * interval_multiplier
|
| + # Compute number of locations the will roughly result in the requested number
|
| + # of intervals.
|
| + location_count = int(math.ceil(math.sqrt(interval_count*2)))
|
| + inv_sample_count = 1.0 / len(samples)
|
| +
|
| + # Generate list of equally spaced locations.
|
| + for i in xrange(0, location_count):
|
| + location = float(i) / (location_count-1)
|
| + locations.append(location)
|
| + left.append(bisect.bisect_left(samples, location))
|
| + right.append(bisect.bisect_right(samples, location))
|
| +
|
| + # Iterate over the intervals defined by any pair of locations.
|
| + for i in xrange(0, len(locations)):
|
| + for j in xrange(i, len(locations)):
|
| + # Compute length of interval and number of samples in the interval.
|
| + length = locations[j] - locations[i]
|
| + count = right[j] - left[i]
|
| +
|
| + # Compute local discrepancy and update max_local_discrepancy.
|
| + local_discrepancy = abs(float(count)*inv_sample_count - length)
|
| + max_local_discrepancy = max(local_discrepancy, max_local_discrepancy)
|
| +
|
| + return max_local_discrepancy
|
| +
|
| +def FrameDiscrepancy(frame_timestamps, absolute = True,
|
| + interval_multiplier = 10000):
|
| + ''' A discrepancy based metric for measuring jank.
|
| +
|
| + FrameDiscrepancy quantifies the largest area of jank observed in a series
|
| + of timestamps. Note that this is different form metrics based on the
|
| + max_frame_time. For example, the time stamp series A = [0,1,2,3,5,6] and
|
| + B = [0,1,2,3,5,7] have the same max_frame_time = 2, but
|
| + Discrepancy(B) > Discrepancy(A).
|
| +
|
| + Two variants of discrepancy can be computed:
|
| +
|
| + Relative discrepancy is following the original definition of
|
| + discrepancy. It characterized the largest area of jank, relative to the
|
| + duration of the entire time stamp series. We normalize the raw results,
|
| + because the best case discrepancy for a set of N samples is 1/N (for
|
| + equally spaced samples), and we want our metric to report 0.0 in that
|
| + case.
|
| +
|
| + Absolute discrepancy also characterizes the largest area of jank, but its
|
| + value wouldn't change (except for imprecisions due to a low
|
| + interval_multiplier) if additional 'good' frames were added to an
|
| + exisiting list of time stamps. Its range is [0,inf] and the unit is
|
| + milliseconds.
|
| +
|
| + The time stamp series C = [0,2,3,4] and D = [0,2,3,4,5] have the same
|
| + absolute discrepancy, but D has lower relative discrepancy than C.
|
| + '''
|
| + samples, sample_scale = NormalizeSamples(frame_timestamps)
|
| + discrepancy = Discrepancy(samples, interval_multiplier)
|
| + inv_sample_count = 1.0 / len(samples)
|
| + if absolute:
|
| + # Compute absolute discrepancy
|
| + discrepancy /= sample_scale
|
| + else:
|
| + # Compute relative discrepancy
|
| + discrepancy = Clamp((discrepancy-inv_sample_count) / (1.0-inv_sample_count))
|
| + return discrepancy
|
|
|