/*
|
* Copyright (C) 2018 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package com.android.server.wifi.util;
|
|
import android.util.SparseIntArray;
|
|
/**
|
* Utilities for Metrics collections.
|
*/
|
public class MetricsUtils {
|
/**
|
* A generic bucket containing a start, end, and count. The utility classes will convert to
|
* such a generic bucket which can then be copied into the specific bucket of the proto.
|
*/
|
public static class GenericBucket {
|
public long start;
|
public long end;
|
public int count;
|
}
|
|
/**
|
* Specifies a ~log histogram consisting of two levels of buckets - a set of N big buckets:
|
*
|
* Buckets starts at: B + P * M^i, where i=0, ... , N-1 (N big buckets)
|
* Each big bucket is divided into S sub-buckets
|
*
|
* Each (big) bucket is M times bigger than the previous one.
|
*
|
* The buckets are then:
|
* #0: B + P * M^0 with S buckets each of width (P*M^1-P*M^0)/S
|
* #1: B + P * M^1 with S buckets each of width (P*M^2-P*M^1)/S
|
* ...
|
* #N-1: B + P * M^(N-1) with S buckets each of width (P*M^N-P*M^(N-1))/S
|
*/
|
public static class LogHistParms {
|
public LogHistParms(int b, int p, int m, int s, int n) {
|
this.b = b;
|
this.p = p;
|
this.m = m;
|
this.s = s;
|
this.n = n;
|
|
// derived values
|
mLog = Math.log(m);
|
bb = new double[n];
|
sbw = new double[n];
|
bb[0] = b + p;
|
sbw[0] = p * (m - 1.0) / (double) s;
|
for (int i = 1; i < n; ++i) {
|
bb[i] = m * (bb[i - 1] - b) + b;
|
sbw[i] = m * sbw[i - 1];
|
}
|
}
|
|
// spec
|
public int b;
|
public int p;
|
public int m;
|
public int s;
|
public int n;
|
|
// derived
|
public double mLog;
|
public double[] bb; // bucket base
|
public double[] sbw; // sub-bucket width
|
}
|
|
/**
|
* Adds the input value to the log histogram based on the histogram parameters.
|
*/
|
public static int addValueToLogHistogram(long x, SparseIntArray histogram, LogHistParms hp) {
|
double logArg = (double) (x - hp.b) / (double) hp.p;
|
int bigBucketIndex = -1;
|
if (logArg > 0) {
|
bigBucketIndex = (int) (Math.log(logArg) / hp.mLog);
|
}
|
int subBucketIndex;
|
if (bigBucketIndex < 0) {
|
bigBucketIndex = 0;
|
subBucketIndex = 0;
|
} else if (bigBucketIndex >= hp.n) {
|
bigBucketIndex = hp.n - 1;
|
subBucketIndex = hp.s - 1;
|
} else {
|
subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]);
|
if (subBucketIndex >= hp.s) { // probably a rounding error so move to next big bucket
|
bigBucketIndex++;
|
if (bigBucketIndex >= hp.n) {
|
bigBucketIndex = hp.n - 1;
|
subBucketIndex = hp.s - 1;
|
} else {
|
subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]);
|
}
|
}
|
}
|
int key = bigBucketIndex * hp.s + subBucketIndex;
|
|
// note that get() returns 0 if index not there already
|
int newValue = histogram.get(key) + 1;
|
histogram.put(key, newValue);
|
|
return newValue;
|
}
|
|
/**
|
* Converts the log histogram (with the specified histogram parameters) to an array of generic
|
* histogram buckets.
|
*/
|
public static GenericBucket[] logHistogramToGenericBuckets(SparseIntArray histogram,
|
LogHistParms hp) {
|
GenericBucket[] protoArray = new GenericBucket[histogram.size()];
|
for (int i = 0; i < histogram.size(); ++i) {
|
int key = histogram.keyAt(i);
|
|
protoArray[i] = new GenericBucket();
|
protoArray[i].start = (long) (hp.bb[key / hp.s] + hp.sbw[key / hp.s] * (key % hp.s));
|
protoArray[i].end = (long) (protoArray[i].start + hp.sbw[key / hp.s]);
|
protoArray[i].count = histogram.valueAt(i);
|
}
|
|
return protoArray;
|
}
|
|
/**
|
* Adds the input value to the histogram based on the lineaer histogram parameters.
|
*
|
* The 'int[] hp' contains a list of bucket limits. The number of buckets is hp.length() + 1
|
* where buckets are:
|
* - < hp[0]
|
* - [hp[0], hp[1])
|
* ...
|
* - >= hp[hp.length() - 1]
|
*/
|
public static int addValueToLinearHistogram(int x, SparseIntArray histogram, int[] hp) {
|
int bucket = 0;
|
for (int limit : hp) {
|
if (x >= limit) {
|
bucket++;
|
continue;
|
}
|
break;
|
}
|
|
// note that get() returns 0 if index not there already
|
int newValue = histogram.get(bucket) + 1;
|
histogram.put(bucket, newValue);
|
|
return newValue;
|
}
|
|
/**
|
* Converts the histogram (with the specified linear histogram parameters) to an array of
|
* generic histogram buckets.
|
*/
|
public static GenericBucket[] linearHistogramToGenericBuckets(SparseIntArray histogram,
|
int[] linearHistParams) {
|
GenericBucket[] protoArray = new GenericBucket[histogram.size()];
|
for (int i = 0; i < histogram.size(); ++i) {
|
int bucket = histogram.keyAt(i);
|
|
protoArray[i] = new GenericBucket();
|
if (bucket == 0) {
|
protoArray[i].start = Integer.MIN_VALUE;
|
protoArray[i].end = linearHistParams[0];
|
} else if (bucket != linearHistParams.length) {
|
protoArray[i].start = linearHistParams[bucket - 1];
|
protoArray[i].end = linearHistParams[bucket];
|
} else {
|
protoArray[i].start = linearHistParams[linearHistParams.length - 1];
|
protoArray[i].end = Integer.MAX_VALUE;
|
}
|
protoArray[i].count = histogram.valueAt(i);
|
}
|
|
return protoArray;
|
}
|
}
|