-
Notifications
You must be signed in to change notification settings - Fork 48
/
Copy pathCounter.cs
118 lines (98 loc) · 3.63 KB
/
Counter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
namespace ServiceControl.Infrastructure.Metrics
{
using System;
using System.Diagnostics;
using System.Threading;
public class Counter
{
readonly string name;
readonly bool enabled;
readonly int[] eventsPerSecond;
readonly int[] movingAverage;
readonly long[] movingAverageEpochs;
long epoch;
internal Counter(string name, bool enabled)
{
this.name = name;
this.enabled = enabled;
eventsPerSecond = new int[2];
movingAverage = new int[300];
movingAverageEpochs = new long[300];
epoch = DateTime.UtcNow.Minute;
}
public void Mark()
{
if (!enabled)
{
return;
}
var ticks = Stopwatch.GetTimestamp();
var currentEpoch = ticks / Stopwatch.Frequency;
var bucketTierIndex = currentEpoch % 2;
if (InterlockedExchangeIfGreaterThan(ref epoch, currentEpoch, currentEpoch, out var previousEpoch))
{
var previousBucketTierIndex = previousEpoch % 2;
var previousEpochTotal = eventsPerSecond[previousBucketTierIndex];
eventsPerSecond[previousBucketTierIndex] = 0;
Interlocked.Increment(ref eventsPerSecond[bucketTierIndex]);
AddMovingAverageValue(previousEpochTotal, previousEpoch);
}
else
{
Interlocked.Increment(ref eventsPerSecond[bucketTierIndex]);
}
}
public MeterValues GetValues()
{
var ticks = Stopwatch.GetTimestamp();
var currentEpoch = ticks / Stopwatch.Frequency;
var currentValueIndex = Array.IndexOf(movingAverageEpochs, currentEpoch - 1);
var currentValue = currentValueIndex != -1 ? movingAverage[currentValueIndex] : 0;
var threshold15 = currentEpoch - 15;
var threshold60 = currentEpoch - 60;
var threshold300 = currentEpoch - 300;
var count15 = 0;
var count60 = 0;
var count300 = 0;
for (var i = 0; i < movingAverage.Length; i++)
{
if (movingAverageEpochs[i] > threshold300)
{
var v = movingAverage[i];
count300 += v;
if (movingAverageEpochs[i] > threshold60)
{
count60 += v;
if (movingAverageEpochs[i] > threshold15)
{
count15 += v;
}
}
}
}
return new MeterValues(name, currentValue, count15 / 15f, count60 / 60f, count300 / 300f);
}
void AddMovingAverageValue(int previousEpochTotal, long previousEpoch)
{
var bucket = previousEpoch % 300;
movingAverage[bucket] = previousEpochTotal;
movingAverageEpochs[bucket] = previousEpoch;
}
public static bool InterlockedExchangeIfGreaterThan(ref long location, long comparison, long newValue, out long previous)
{
long initialValue;
do
{
initialValue = location;
if (initialValue >= comparison)
{
previous = -1;
return false;
}
}
while (Interlocked.CompareExchange(ref location, newValue, initialValue) != initialValue);
previous = initialValue;
return true;
}
}
}