1 #region Copyright notice and license 2 3 // Copyright 2015 gRPC authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #endregion 18 19 using System; 20 using System.Collections.Generic; 21 using System.Diagnostics; 22 using System.IO; 23 using System.Linq; 24 using System.Text.RegularExpressions; 25 using System.Threading; 26 using System.Threading.Tasks; 27 using Google.Protobuf; 28 using Grpc.Core; 29 using Grpc.Core.Utils; 30 using NUnit.Framework; 31 using Grpc.Testing; 32 33 namespace Grpc.IntegrationTesting 34 { 35 /// <summary> 36 /// Basic implementation of histogram based on grpc/support/histogram.h. 37 /// </summary> 38 public class Histogram 39 { 40 readonly object myLock = new object(); 41 readonly double multiplier; 42 readonly double oneOnLogMultiplier; 43 readonly double maxPossible; 44 readonly uint[] buckets; 45 46 int count; 47 double sum; 48 double sumOfSquares; 49 double min; 50 double max; 51 Histogram(double resolution, double maxPossible)52 public Histogram(double resolution, double maxPossible) 53 { 54 GrpcPreconditions.CheckArgument(resolution > 0); 55 GrpcPreconditions.CheckArgument(maxPossible > 0); 56 this.maxPossible = maxPossible; 57 this.multiplier = 1.0 + resolution; 58 this.oneOnLogMultiplier = 1.0 / Math.Log(1.0 + resolution); 59 this.buckets = new uint[FindBucket(maxPossible) + 1]; 60 61 ResetUnsafe(); 62 } 63 AddObservation(double value)64 public void AddObservation(double value) 65 { 66 lock (myLock) 67 { 68 AddObservationUnsafe(value); 69 } 70 } 71 72 /// <summary> 73 /// Gets snapshot of stats and optionally resets the histogram. 74 /// </summary> GetSnapshot(bool reset = false)75 public HistogramData GetSnapshot(bool reset = false) 76 { 77 lock (myLock) 78 { 79 var histogramData = new HistogramData(); 80 GetSnapshotUnsafe(histogramData, reset); 81 return histogramData; 82 } 83 } 84 85 /// <summary> 86 /// Merges snapshot of stats into <c>mergeTo</c> and optionally resets the histogram. 87 /// </summary> GetSnapshot(HistogramData mergeTo, bool reset)88 public void GetSnapshot(HistogramData mergeTo, bool reset) 89 { 90 lock (myLock) 91 { 92 GetSnapshotUnsafe(mergeTo, reset); 93 } 94 } 95 96 /// <summary> 97 /// Finds bucket index to which given observation should go. 98 /// </summary> FindBucket(double value)99 private int FindBucket(double value) 100 { 101 value = Math.Max(value, 1.0); 102 value = Math.Min(value, this.maxPossible); 103 return (int)(Math.Log(value) * oneOnLogMultiplier); 104 } 105 AddObservationUnsafe(double value)106 private void AddObservationUnsafe(double value) 107 { 108 this.count++; 109 this.sum += value; 110 this.sumOfSquares += value * value; 111 this.min = Math.Min(this.min, value); 112 this.max = Math.Max(this.max, value); 113 114 this.buckets[FindBucket(value)]++; 115 } 116 GetSnapshotUnsafe(HistogramData mergeTo, bool reset)117 private void GetSnapshotUnsafe(HistogramData mergeTo, bool reset) 118 { 119 GrpcPreconditions.CheckArgument(mergeTo.Bucket.Count == 0 || mergeTo.Bucket.Count == buckets.Length); 120 if (mergeTo.Count == 0) 121 { 122 mergeTo.MinSeen = min; 123 mergeTo.MaxSeen = max; 124 } 125 else 126 { 127 mergeTo.MinSeen = Math.Min(mergeTo.MinSeen, min); 128 mergeTo.MaxSeen = Math.Max(mergeTo.MaxSeen, max); 129 } 130 mergeTo.Count += count; 131 mergeTo.Sum += sum; 132 mergeTo.SumOfSquares += sumOfSquares; 133 134 if (mergeTo.Bucket.Count == 0) 135 { 136 mergeTo.Bucket.AddRange(buckets); 137 } 138 else 139 { 140 for (int i = 0; i < buckets.Length; i++) 141 { 142 mergeTo.Bucket[i] += buckets[i]; 143 } 144 } 145 146 if (reset) 147 { 148 ResetUnsafe(); 149 } 150 } 151 ResetUnsafe()152 private void ResetUnsafe() 153 { 154 this.count = 0; 155 this.sum = 0; 156 this.sumOfSquares = 0; 157 this.min = double.PositiveInfinity; 158 this.max = double.NegativeInfinity; 159 for (int i = 0; i < this.buckets.Length; i++) 160 { 161 this.buckets[i] = 0; 162 } 163 } 164 } 165 } 166