nfv/nfv/nfv-common/nfv_common/histogram.py

188 lines
5.2 KiB
Python
Executable File

#
# Copyright (c) 2015-2016 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import math
import array
import datetime
from nfv_common import debug
DLOG = debug.debug_get_logger('nfv_common.histogram')
class Histogram(object):
"""
Histogram Object
"""
def __init__(self, name, num_buckets, units):
self._name = name
self._units = units
self._num_buckets = num_buckets
self._created_date = datetime.datetime.now()
self._reset_date = self._created_date
self._sample_total = 0
self._num_samples = 0
self._average_sample = None
self._max_sample = -1
self._max_sample_date = None
self._buckets = array.array("L", [0] * num_buckets)
@property
def name(self):
"""
Returns the name of the histogram
"""
return self._name
def add_data(self, sample):
"""
Convert data given to the nearest power of two.
"""
sample_as_int = int(sample)
if 0 == sample_as_int:
bucket_idx = sample_as_int.bit_length()
else:
bucket_idx = (sample_as_int - 1).bit_length()
if bucket_idx > self._num_buckets:
bucket_idx = self._num_buckets - 1
if sample_as_int > self._max_sample:
self._max_sample = sample_as_int
self._max_sample_date = datetime.datetime.now()
self._sample_total += sample_as_int
self._num_samples += 1
self._average_sample = (self._sample_total / self._num_samples)
self._buckets[bucket_idx] += 1
def reset_data(self):
"""
Clear out the collected samples.
"""
self._reset_date = datetime.datetime.now()
self._sample_total = 0
self._num_samples = 0
self._average_sample = None
self._max_sample = -1
self._max_sample_date = None
for idx, _ in enumerate(self._buckets):
self._buckets[idx] = 0
@staticmethod
def _scale_sample(scale_min, scale_max, sample_min, sample_max, sample):
"""
Normalize sample to be compared with other samples
"""
if scale_min == scale_max:
return sample
if sample_min == sample_max:
return sample
return ((((scale_max - scale_min) * (sample - sample_min)) /
(sample_max - sample_min)) + sample_max)
def display_data(self, pretty_format=True):
"""
Output the histogram to a log.
"""
date_str = ""
values_str = ""
if pretty_format:
date_str += " created-date: %s" % self._created_date
if self._reset_date is not None:
date_str += " reset-date: %s" % self._reset_date
values_str += " total: %s" % self._num_samples
if self._average_sample is not None:
values_str += " avg: %s" % self._average_sample
if self._max_sample_date is not None:
values_str += (" max: %s (%s)" % (self._max_sample,
self._max_sample_date))
DLOG.info("%s" % '-' * 120)
DLOG.info("Histogram: %s" % self._name)
if "" != date_str:
DLOG.info("%s" % date_str)
if "" != values_str:
DLOG.info(" %s" % values_str)
for idx, bucket_value in enumerate(self._buckets):
if 0 != bucket_value:
if pretty_format:
scaled_bucket_value \
= self._scale_sample(0, 60, 0, self._max_sample,
bucket_value)
DLOG.info(" %03i [up to %03i %s]: %07i %s"
% (idx, math.pow(2, idx), self._units, bucket_value,
'*' * min(60, scaled_bucket_value)))
else:
DLOG.info(" %03i [up to %03i %s]: %07i"
% (idx, math.pow(2, idx), self._units, bucket_value))
if pretty_format:
DLOG.info("%s" % '-' * 120)
_histograms = dict()
def _find_histogram(name):
"""
Lookup a histogram with a particular name
"""
if name in _histograms:
return _histograms[name]
return None
def add_histogram_data(name, sample, units):
"""
Add a sample to a histogram
"""
global _histograms
histogram = _find_histogram(name)
if histogram is None:
histogram = Histogram(name, 16, units)
_histograms[name] = histogram
histogram.add_data(sample)
def reset_histogram_data(name=None):
"""
Reset histogram data
"""
if name is None:
for histogram in _histograms.itervalues():
histogram.reset_data()
else:
histogram = _find_histogram(name)
if histogram is not None:
histogram.reset_data()
def display_histogram_data(name=None, pretty_format=True):
"""
Display histogram data captured
"""
if name is None:
for histogram in _histograms.itervalues():
histogram.display_data(pretty_format)
else:
histogram = _find_histogram(name)
if histogram is not None:
histogram.display_data(pretty_format)