/
IntervalHistogramRecorder.java
171 lines (154 loc) · 8.31 KB
/
IntervalHistogramRecorder.java
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/**
* Written by Gil Tene of Azul Systems, and released to the public domain,
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
*
* @author Gil Tene
*/
package org.HdrHistogram;
/**
* {@link IntervalHistogramRecorder} records values, and provides stable interval histograms from
* live recorded data without interrupting or stalling active recording of values. Each interval
* histogram provided contains all value counts accumulated since the previous interval histogram
* was taken.
* <p>
* This pattern is commonly used in logging interval histogram information while recoding is ongoing.
* <p>
* {@link IntervalHistogramRecorder} supports concurrent
* {@link org.HdrHistogram.IntervalHistogramRecorder#recordValue} or
* {@link org.HdrHistogram.IntervalHistogramRecorder#recordValueWithExpectedInterval} calls.
* Recording calls are wait-free on architectures that support atomic increment operations, and
* are lock-free on architectures that do no.
*
*/
public class IntervalHistogramRecorder {
private final WriterReaderPhaser recordingPhaser = new WriterReaderPhaser();
private volatile AtomicHistogram activeHistogram;
private AtomicHistogram inactiveHistogram;
/**
* Construct a DoubleBufferedHistogram given the highest value to be tracked and a number of significant
* decimal digits. The histogram will be constructed to implicitly track (distinguish from 0) values as low as 1.
*
* @param highestTrackableValue The highest value to be tracked by the histogram. Must be a positive
* integer that is {@literal >=} 2.
* @param 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.
*/
public IntervalHistogramRecorder(final long highestTrackableValue,
final int numberOfSignificantValueDigits) {
this(1, highestTrackableValue, numberOfSignificantValueDigits);
}
/**
* Construct a DoubleBufferedHistogram given the Lowest and highest values to be tracked and a number
* of significant decimal digits. Providing a lowestDiscernibleValue is useful is situations where the units used
* for the histogram's values are much smaller that the minimal accuracy required. E.g. when tracking
* time values stated in nanosecond units, where the minimal accuracy required is a microsecond, the
* proper value for lowestDiscernibleValue would be 1000.
*
* @param lowestDiscernibleValue The lowest value that can be tracked (distinguished from 0) by the histogram.
* Must be a positive integer that is {@literal >=} 1. May be internally rounded down to nearest
* power of 2.
* @param highestTrackableValue The highest value to be tracked by the histogram. Must be a positive
* integer that is {@literal >=} (2 * lowestDiscernibleValue).
* @param 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.
*/
public IntervalHistogramRecorder(final long lowestDiscernibleValue,
final long highestTrackableValue,
final int numberOfSignificantValueDigits) {
activeHistogram = new AtomicHistogram(
lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits);
inactiveHistogram = new AtomicHistogram(
lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits);
}
/**
* Record a value
* @param value the value to record
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
public void recordValue(long value) {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
try {
activeHistogram.recordValue(value);
} finally {
recordingPhaser.writerCriticalSectionExit(criticalValueAtEnter);
}
}
/**
* Record a value
* <p>
* To compensate for the loss of sampled values when a recorded value is larger than the expected
* interval between value samples, Histogram will auto-generate an additional series of decreasingly-smaller
* (down to the expectedIntervalBetweenValueSamples) value records.
* <p>
* See related notes {@link AbstractHistogram#recordValueWithExpectedInterval(long, long)}
* for more explanations about coordinated opmissionand expetced interval correction.
* *
* @param value The value to record
* @param expectedIntervalBetweenValueSamples If expectedIntervalBetweenValueSamples is larger than 0, add
* auto-generated value records as appropriate if value is larger
* than expectedIntervalBetweenValueSamples
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
public void recordValueWithExpectedInterval(final long value, final long expectedIntervalBetweenValueSamples)
throws ArrayIndexOutOfBoundsException {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
try {
activeHistogram.recordValueWithExpectedInterval(value, expectedIntervalBetweenValueSamples);
} finally {
recordingPhaser.writerCriticalSectionExit(criticalValueAtEnter);
}
}
/**
* Get a new interval histogram, which will include a stable, consistent view of all value counts
* accumulated since the last interval histogram was taken.
* <p>
* Calling {@link org.HdrHistogram.IntervalHistogramRecorder#getIntervalHistogram}() will reset
* the value counts, and start accumulating value counts for the next interval.
*
* @return a histogram containing the value counts accumulated since the last interval histogram was taken.
*/
public synchronized Histogram getIntervalHistogram() {
Histogram intervalHistogram = new Histogram(inactiveHistogram);
getIntervalHistogramInto(intervalHistogram);
return intervalHistogram;
}
/**
* Place a copy of the value counts accumulated since accumulated (since the last interval histogram
* was taken) into {@code targetHistogram}.
*
* Calling {@link org.HdrHistogram.IntervalHistogramRecorder#getIntervalHistogramInto}() will reset
* the value counts, and start accumulating value counts for the next interval.
*
* @param targetHistogram the histogram into which the interval histogram's data should be copied
*/
public synchronized void getIntervalHistogramInto(AbstractHistogram targetHistogram) {
performIntervalSample();
inactiveHistogram.copyInto(targetHistogram);
}
public synchronized void reset() {
// the currently inactive histogram is reset each time we flip. So flipping twice resets both:
performIntervalSample();
performIntervalSample();
}
private void performIntervalSample() {
inactiveHistogram.reset();
try {
recordingPhaser.readerLock();
// Swap active and inactive histograms:
final AtomicHistogram tempHistogram = inactiveHistogram;
inactiveHistogram = activeHistogram;
activeHistogram = tempHistogram;
// Mark end time of previous interval and start time of new one:
long now = System.currentTimeMillis();
activeHistogram.setStartTimeStamp(now);
inactiveHistogram.setEndTimeStamp(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(500000L /* yield in 0.5 msec units if needed */);
} finally {
recordingPhaser.readerUnlock();
}
}
}