/// <summary> /// Add the contents of another histogram to this one. /// </summary> /// <param name="fromHistogram">The other histogram.</param> /// <exception cref="System.IndexOutOfRangeException">if values in fromHistogram's are higher than highestTrackableValue.</exception> public virtual void Add(HistogramBase fromHistogram) { if (HighestTrackableValue < fromHistogram.HighestTrackableValue) { throw new ArgumentOutOfRangeException(nameof(fromHistogram), $"The other histogram covers a wider range ({fromHistogram.HighestTrackableValue} than this one ({HighestTrackableValue})."); } if ((BucketCount == fromHistogram.BucketCount) && (SubBucketCount == fromHistogram.SubBucketCount) && (_unitMagnitude == fromHistogram._unitMagnitude)) { // Counts arrays are of the same length and meaning, so we can just iterate and add directly: for (var i = 0; i < fromHistogram.CountsArrayLength; i++) { AddToCountAtIndex(i, fromHistogram.GetCountAtIndex(i)); } } else { // Arrays are not a direct match, so we can't just stream through and add them. // Instead, go through the array and add each non-zero value found at it's proper value: for (var i = 0; i < fromHistogram.CountsArrayLength; i++) { var count = fromHistogram.GetCountAtIndex(i); RecordValueWithCount(fromHistogram.ValueFromIndex(i), count); } } }
/// <summary> /// Copy this histogram into the target histogram, overwriting it's contents. /// </summary> /// <param name="source">The source histogram</param> /// <param name="targetHistogram">the histogram to copy into</param> public static void CopyInto(this HistogramBase source, HistogramBase targetHistogram) { targetHistogram.Reset(); targetHistogram.Add(source); targetHistogram.StartTimeStamp = source.StartTimeStamp; targetHistogram.EndTimeStamp = source.EndTimeStamp; }
private void ValidateFitAsReplacementHistogram(HistogramBase replacementHistogram) { if (replacementHistogram != null && replacementHistogram.InstanceId != _activeHistogram.InstanceId) { throw new InvalidOperationException( $"Replacement histogram must have been obtained via a previous getIntervalHistogram() call from this {GetType().Name} instance"); } }
/// <summary> /// Place a copy of the value counts accumulated since accumulated (since the last interval histogram was taken) into <paramref name="targetHistogram"/>. /// This will overwrite the existing data in <paramref name="targetHistogram"/>. /// Calling <see cref="GetIntervalHistogramInto(HistogramBase)"/> will reset the value counts, and start accumulating value counts for the next interval. /// </summary> /// <param name="targetHistogram">The histogram into which the interval histogram's data should be copied.</param> public void GetIntervalHistogramInto(HistogramBase targetHistogram) { lock (_gate) { PerformIntervalSample(); _inactiveHistogram.CopyInto(targetHistogram); } }
/// <summary> /// Creates a recorder that will delegate recording to histograms created from these parameters. /// </summary> /// <param name="lowestDiscernibleValue">The lowest value that can be tracked (distinguished from 0) by the histogram. /// Must be a positive integer that is >= 1. /// May be internally rounded down to nearest power of 2. /// </param> /// <param name="highestTrackableValue">The highest value to be tracked by the histogram. /// Must be a positive integer that is >= (2 * lowestTrackableValue). /// </param> /// <param name="numberOfSignificantValueDigits"> /// The number of significant decimal digits to which the histogram will maintain value resolution and separation. /// Must be a non-negative integer between 0 and 5. /// </param> /// <param name="histogramFactory">The factory to be used to actually create instances of <seealso cref="HistogramBase"/>.</param> public Recorder( long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits, HistogramFactoryDelegate histogramFactory) { _histogramFactory = histogramFactory; _activeHistogram = histogramFactory(_instanceId, lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); _inactiveHistogram = histogramFactory(_instanceId, lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); _activeHistogram.StartTimeStamp = DateTime.Now.MillisecondsSinceUnixEpoch(); }
/// <summary> /// Get the computed mean value of all recorded values in the histogram /// </summary> /// <returns>the mean value (in value units) of the histogram data</returns> public static double GetMean(this HistogramBase histogram) { var totalCount = histogram.TotalCount; if (totalCount == 0) { return(0); } var totalValue = histogram.RecordedValues().Select(hiv => hiv.TotalValueToThisValue).LastOrDefault(); return((totalValue * 1.0) / totalCount); }
/// <summary> /// Get a new instance of an interval histogram, which will include a stable, consistent view of all value counts accumulated since the last interval histogram was taken. /// Calling <see cref="GetIntervalHistogram()"/> will reset the value counts, and start accumulating value counts for the next interval. /// </summary> /// <param name="histogramToRecycle">a previously returned interval histogram that may be recycled to avoid allocation and copy operations.</param> /// <returns>A histogram containing the value counts accumulated since the last interval histogram was taken.</returns> /// <remarks> /// <see cref="GetIntervalHistogram(HistogramBase)"/> accepts a previously returned interval histogram that can be recycled internally to avoid allocation and content copying operations. /// It is therefore significantly more efficient for repeated use than <see cref="GetIntervalHistogram()"/> and <see cref="GetIntervalHistogramInto(HistogramBase)"/>. /// The provided <paramref name="histogramToRecycle"/> must be either be null or an interval histogram returned by a previous call to <see cref="GetIntervalHistogram(HistogramBase)"/> or <see cref="GetIntervalHistogram()"/>. /// NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. /// If the same interval histogram instance is recycled more than once, behavior is undefined. /// </remarks> public HistogramBase GetIntervalHistogram(HistogramBase histogramToRecycle) { lock (_gate) { // Verify that replacement histogram can validly be used as an inactive histogram replacement: ValidateFitAsReplacementHistogram(histogramToRecycle); _inactiveHistogram = histogramToRecycle; PerformIntervalSample(); var sampledHistogram = _inactiveHistogram; _inactiveHistogram = null; // Once we expose the sample, we can't reuse it internally until it is recycled return(sampledHistogram); } }
/// <summary> /// Get the computed standard deviation of all recorded values in the histogram /// </summary> /// <returns>the standard deviation (in value units) of the histogram data</returns> public static double GetStdDeviation(this HistogramBase histogram) { var mean = histogram.GetMean(); var geometricDeviationTotal = 0.0; foreach (var iterationValue in histogram.RecordedValues()) { double deviation = (histogram.MedianEquivalentValue(iterationValue.ValueIteratedTo) * 1.0) - mean; geometricDeviationTotal += (deviation * deviation) * iterationValue.CountAddedInThisIterationStep; } var stdDeviation = Math.Sqrt(geometricDeviationTotal / histogram.TotalCount); return(stdDeviation); }
private void PerformIntervalSample() { try { _recordingPhaser.ReaderLock(); // Make sure we have an inactive version to flip in: if (_inactiveHistogram == null) { _inactiveHistogram = _histogramFactory(_instanceId, _activeHistogram.LowestTrackableValue, _activeHistogram.HighestTrackableValue, _activeHistogram.NumberOfSignificantValueDigits); } _inactiveHistogram.Reset(); // Swap active and inactive histograms: var tempHistogram = _inactiveHistogram; _inactiveHistogram = _activeHistogram; _activeHistogram = tempHistogram; // Mark end time of previous interval and start time of new one: var now = DateTime.Now.MillisecondsSinceUnixEpoch(); _activeHistogram.StartTimeStamp = now; _inactiveHistogram.EndTimeStamp = now; // Make sure we are not in the middle of recording a value on the previously active histogram: // Flip phase to make sure no recordings that were in flight pre-flip are still active: _recordingPhaser.FlipPhase(TimeSpan.FromMilliseconds(0.5)); } finally { _recordingPhaser.ReaderUnlock(); } }
/// <summary> /// Get the highest value that is equivalent to the given value within the histogram's resolution. /// Where "equivalent" means that value samples recorded for any two equivalent values are counted in a common /// total count. /// </summary> /// <param name="histogram">The histogram to operate on</param> /// <param name="value">The given value</param> /// <returns>The highest value that is equivalent to the given value within the histogram's resolution.</returns> public static long HighestEquivalentValue(this HistogramBase histogram, long value) { return(histogram.NextNonEquivalentValue(value) - 1); }
/// <summary> /// Get the highest recorded value level in the histogram /// </summary> /// <returns>the Max value recorded in the histogram</returns> public static long GetMaxValue(this HistogramBase histogram) { var max = histogram.RecordedValues().Select(hiv => hiv.ValueIteratedTo).LastOrDefault(); return(histogram.HighestEquivalentValue(max)); }
/// <summary> /// Provide a means of iterating through histogram values according to percentile levels. /// The iteration is performed in steps that start at 0% and reduce their distance to 100% according to the /// <paramref name="percentileTicksPerHalfDistance"/> parameter, ultimately reaching 100% when all recorded /// histogram values are exhausted. /// </summary> /// <param name="histogram">The histogram to operate on</param> /// <param name="percentileTicksPerHalfDistance"> /// The number of iteration steps per half-distance to 100%. /// </param> /// <returns> /// An enumerator of <see cref="HistogramIterationValue"/> through the histogram using a /// <see cref="PercentileEnumerator"/>. /// </returns> public static IEnumerable <HistogramIterationValue> Percentiles(this HistogramBase histogram, int percentileTicksPerHalfDistance) { return(new PercentileEnumerable(histogram, percentileTicksPerHalfDistance)); }
//Optimization. This simple method should be in-lined by the JIT compiler, allowing hot path `GetBucketIndex(long, long, int)` to become static. -LC private int GetBucketIndex(long value) { return(HistogramBase.GetBucketIndex(value, _subBucketMask, _bucketIndexOffset)); }