aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/MetricsCollector.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/MetricsCollector.cs')
-rw-r--r--OpenSim/Framework/MetricsCollector.cs223
1 files changed, 223 insertions, 0 deletions
diff --git a/OpenSim/Framework/MetricsCollector.cs b/OpenSim/Framework/MetricsCollector.cs
new file mode 100644
index 0000000..c8f4a33
--- /dev/null
+++ b/OpenSim/Framework/MetricsCollector.cs
@@ -0,0 +1,223 @@
1using System;
2using System.Diagnostics;
3
4namespace OpenSim.Framework
5{
6 /// <summary>
7 /// A MetricsCollector for 'long' values.
8 /// </summary>
9 public class MetricsCollectorLong : MetricsCollector<long>
10 {
11 public MetricsCollectorLong(int windowSize, int numBuckets)
12 : base(windowSize, numBuckets)
13 {
14 }
15
16 protected override long GetZero() { return 0; }
17
18 protected override long Add(long a, long b) { return a + b; }
19 }
20
21
22 /// <summary>
23 /// A MetricsCollector for time spans.
24 /// </summary>
25 public class MetricsCollectorTime : MetricsCollectorLong
26 {
27 public MetricsCollectorTime(int windowSize, int numBuckets)
28 : base(windowSize, numBuckets)
29 {
30 }
31
32 public void AddSample(Stopwatch timer)
33 {
34 long ticks = timer.ElapsedTicks;
35 if (ticks > 0)
36 AddSample(ticks);
37 }
38
39 public TimeSpan GetSumTime()
40 {
41 return TimeSpan.FromMilliseconds((GetSum() * 1000) / Stopwatch.Frequency);
42 }
43 }
44
45
46 struct MetricsBucket<T>
47 {
48 public T value;
49 public int count;
50 }
51
52
53 /// <summary>
54 /// Collects metrics in a sliding window.
55 /// </summary>
56 /// <remarks>
57 /// MetricsCollector provides the current Sum of the metrics that it collects. It can easily be extended
58 /// to provide the Average, too. It uses a sliding window to keep these values current.
59 ///
60 /// This class is not thread-safe.
61 ///
62 /// Subclass MetricsCollector to have it use a concrete value type. Override the abstract methods.
63 /// </remarks>
64 public abstract class MetricsCollector<T>
65 {
66 private int bucketSize; // e.g. 3,000 ms
67
68 private MetricsBucket<T>[] buckets;
69
70 private int NumBuckets { get { return buckets.Length; } }
71
72
73 // The number of the current bucket, if we had an infinite number of buckets and didn't have to wrap around
74 long curBucketGlobal;
75
76 // The total of all the buckets
77 T totalSum;
78 int totalCount;
79
80
81 /// <summary>
82 /// Returns the default (zero) value.
83 /// </summary>
84 /// <returns></returns>
85 protected abstract T GetZero();
86
87 /// <summary>
88 /// Adds two values.
89 /// </summary>
90 protected abstract T Add(T a, T b);
91
92
93 /// <summary>
94 /// Creates a MetricsCollector.
95 /// </summary>
96 /// <param name="windowSize">The period of time over which to collect the metrics, in ms. E.g.: 30,000.</param>
97 /// <param name="numBuckets">The number of buckets to divide the samples into. E.g.: 10. Using more buckets
98 /// smooths the jarring that occurs whenever we drop an old bucket, but uses more memory.</param>
99 public MetricsCollector(int windowSize, int numBuckets)
100 {
101 bucketSize = windowSize / numBuckets;
102 buckets = new MetricsBucket<T>[numBuckets];
103 Reset();
104 }
105
106 public void Reset()
107 {
108 ZeroBuckets(0, NumBuckets);
109 curBucketGlobal = GetNow() / bucketSize;
110 totalSum = GetZero();
111 totalCount = 0;
112 }
113
114 public void AddSample(T sample)
115 {
116 MoveWindow();
117
118 int curBucket = (int)(curBucketGlobal % NumBuckets);
119 buckets[curBucket].value = Add(buckets[curBucket].value, sample);
120 buckets[curBucket].count++;
121
122 totalSum = Add(totalSum, sample);
123 totalCount++;
124 }
125
126 /// <summary>
127 /// Returns the total values in the collection window.
128 /// </summary>
129 public T GetSum()
130 {
131 // It might have been a while since we last added a sample, so we may need to adjust the window
132 MoveWindow();
133
134 return totalSum;
135 }
136
137 /// <summary>
138 /// Returns the current time in ms.
139 /// </summary>
140 private long GetNow()
141 {
142 return DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
143 }
144
145 /// <summary>
146 /// Clears the values in buckets [offset, offset+num)
147 /// </summary>
148 private void ZeroBuckets(int offset, int num)
149 {
150 for (int i = 0; i < num; i++)
151 {
152 buckets[offset + i].value = GetZero();
153 buckets[offset + i].count = 0;
154 }
155 }
156
157 /// <summary>
158 /// Adjusts the buckets so that the "current bucket" corresponds to the current time.
159 /// This may require dropping old buckets.
160 /// </summary>
161 /// <remarks>
162 /// This method allows for the possibility that we don't get new samples for each bucket, so the
163 /// new bucket may be some distance away from the last used bucket.
164 /// </remarks>
165 private void MoveWindow()
166 {
167 long newBucketGlobal = GetNow() / bucketSize;
168 long bucketsDistance = newBucketGlobal - curBucketGlobal;
169
170 if (bucketsDistance == 0)
171 {
172 // We're still on the same bucket as before
173 return;
174 }
175
176 if (bucketsDistance >= NumBuckets)
177 {
178 // Discard everything
179 Reset();
180 return;
181 }
182
183 int curBucket = (int)(curBucketGlobal % NumBuckets);
184 int newBucket = (int)(newBucketGlobal % NumBuckets);
185
186
187 // Clear all the buckets in this range: (cur, new]
188 int numToClear = (int)bucketsDistance;
189
190 if (curBucket < NumBuckets - 1)
191 {
192 // Clear buckets at the end of the window
193 int num = Math.Min((int)bucketsDistance, NumBuckets - (curBucket + 1));
194 ZeroBuckets(curBucket + 1, num);
195 numToClear -= num;
196 }
197
198 if (numToClear > 0)
199 {
200 // Clear buckets at the beginning of the window
201 ZeroBuckets(0, numToClear);
202 }
203
204 // Move the "current bucket" pointer
205 curBucketGlobal = newBucketGlobal;
206
207 RecalcTotal();
208 }
209
210 private void RecalcTotal()
211 {
212 totalSum = GetZero();
213 totalCount = 0;
214
215 for (int i = 0; i < NumBuckets; i++)
216 {
217 totalSum = Add(totalSum, buckets[i].value);
218 totalCount += buckets[i].count;
219 }
220 }
221
222 }
223}