com.google.pubsub.flic.common.LatencyDistribution.java Source code

Java tutorial

Introduction

Here is the source code for com.google.pubsub.flic.common.LatencyDistribution.java

Source

// Copyright 2016 Google Inc.
//
// 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.google.pubsub.flic.common;

import com.google.common.base.Preconditions;
import java.util.Arrays;
import java.util.List;
import java.util.stream.LongStream;
import org.apache.commons.lang3.ArrayUtils;

/**
 * Takes latency measurements and stores them in buckets for more efficient storage, along with
 * utilities to calculate percentiles for analysis of results.
 */
public class LatencyDistribution {
    public static final double[] LATENCY_BUCKETS = { 0.0, 1.0, 5.0, 10.0, 20.0, 40.0, 60.0, 80.0, 100.0, 150.0,
            200.0, 250.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 1500.0, 2000.0, 5000.0, 10000.0,
            50000.0, 100000.0, 500000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0, Integer.MAX_VALUE };
    private final long[] bucketValues = new long[LATENCY_BUCKETS.length];
    private long count = 0;
    private double mean = 0;
    private double sumOfSquaredDeviation = 0;

    public LatencyDistribution() {
    }

    private static int getNthPercentileIndex(long[] bucketValues, double percentile) {
        Preconditions.checkArgument(percentile > 0.0);
        Preconditions.checkArgument(percentile < 100.0);
        long total = LongStream.of(bucketValues).sum();
        if (total == 0) {
            return 0;
        }
        long count = (long) (total * percentile / 100.0);
        for (int i = LATENCY_BUCKETS.length - 1; i > 0; i--) {
            total -= bucketValues[i];
            if (total < count) {
                return i;
            }
        }
        return -1;
    }

    public static double getNthPercentileUpperBound(long[] bucketValues, double percentile) {
        return LATENCY_BUCKETS[Math.max(0, getNthPercentileIndex(bucketValues, percentile))];
    }

    public static String getNthPercentile(long[] bucketValues, double percentile) {
        int index = getNthPercentileIndex(bucketValues, percentile);
        if (index < 1) {
            return "N/A";
        }
        return LATENCY_BUCKETS[index - 1] + " - " + LATENCY_BUCKETS[index];
    }

    public static String getNthPercentileMidpoint(long[] bucketValues, double percentile) {
        int index = getNthPercentileIndex(bucketValues, percentile);
        if (index < 1) {
            return "N/A";
        }
        return Double.toString((LATENCY_BUCKETS[index - 1] + LATENCY_BUCKETS[index]) / 2);
    }

    public synchronized void reset() {
        for (int i = 0; i < LATENCY_BUCKETS.length; i++) {
            bucketValues[i] = 0;
        }
        count = 0;
        mean = 0;
        sumOfSquaredDeviation = 0;
    }

    public long getCount() {
        return count;
    }

    public double getSumOfSquareDeviations() {
        return sumOfSquaredDeviation;
    }

    public double getMean() {
        return mean;
    }

    long[] getBucketValues() {
        return bucketValues;
    }

    public synchronized LatencyDistribution copy() {
        LatencyDistribution latencyDistribution = new LatencyDistribution();
        latencyDistribution.count = count;
        latencyDistribution.mean = mean;
        latencyDistribution.sumOfSquaredDeviation = sumOfSquaredDeviation;
        System.arraycopy(bucketValues, 0, latencyDistribution.bucketValues, 0, LATENCY_BUCKETS.length);
        return latencyDistribution;
    }

    public List<Long> getBucketValuesAsList() {
        return Arrays.asList(ArrayUtils.toObject(bucketValues));
    }

    public void recordLatency(long latencyMs) {
        synchronized (this) {
            count++;
            double dev = latencyMs - mean;
            mean += dev / count;
            sumOfSquaredDeviation += dev * (latencyMs - mean);
        }

        for (int i = 0; i < LATENCY_BUCKETS.length; i++) {
            double bucket = LATENCY_BUCKETS[i];
            if (latencyMs < bucket) {
                synchronized (this) {
                    bucketValues[i]++;
                }
                return;
            }
        }
        synchronized (this) {
            bucketValues[LATENCY_BUCKETS.length - 1]++;
        }
    }

    public void recordLatencyBatch(long latencyMs, int batchSize) {
        synchronized (this) {
            double dev = latencyMs - mean;
            mean = (mean * count + latencyMs * batchSize) / (count + batchSize);
            count += batchSize;
            sumOfSquaredDeviation += dev * (latencyMs - mean) * batchSize;
        }

        for (int i = 0; i < LATENCY_BUCKETS.length; i++) {
            double bucket = LATENCY_BUCKETS[i];
            if (latencyMs < bucket) {
                synchronized (this) {
                    bucketValues[i] += batchSize;
                }
                return;
            }
        }
        synchronized (this) {
            bucketValues[LATENCY_BUCKETS.length - 1] += batchSize;
        }
    }
}