2424 * {@code lambda} is a constant and {@code time} specifies how long ago the increment happened. A <i>moving</i> rate is simply a rate
2525 * calculated for an every-growing series of increments (typically by updating the previous rate rather than recalculating from scratch).
2626 *
27+ * <p>The times involved in the API of this class can use the caller's choice of units and origin, e.g. they can be millis since the
28+ * epoch as returned by {@link System#currentTimeMillis}, nanos since an arbitrary origin as returned by {@link System#nanoTime, or anything
29+ * else. The only requirement is that the same convention must be used consistently.
30+ *
2731 * <p>This class is thread-safe.
2832 */
2933public class ExponentiallyWeightedMovingRate {
@@ -35,108 +39,108 @@ public class ExponentiallyWeightedMovingRate {
3539 // we can do up to tens of millions of QPS before the lock risks becoming a bottleneck.
3640
3741 private final double lambda ;
38- private final long startTimeInMillis ;
42+ private final long startTime ;
3943 private double rate ;
40- long lastTimeInMillis ;
44+ long lastTime ;
4145
4246 /**
4347 * Constructor.
4448 *
4549 * @param lambda A parameter which dictates how quickly the average "forgets" older increments. The weight given to an increment which
46- * happened a time {@code timeAgo} milliseconds ago will be proportional to {@code exp(-1.0 * lambda * timeAgo)}. The half-life
47- * (measured in milliseconds) is related to this parameter by the equation {@code exp(-1.0 * lambda * halfLife)} = 0.5}, so
48- * {@code lambda = log(2.0) / halfLife)}. This may be zero, but must not be negative .
49- * @param startTimeInMillis The time, in milliseconds since the epoch, to consider the start time for the rate calculation. This must be
50- * greater than zero .
50+ * happened time {@code timeAgo} ago will be proportional to {@code exp(-1.0 * lambda * timeAgo)}. The half-life is related to this
51+ * parameter by the equation {@code exp(-1.0 * lambda * halfLife)} = 0.5}, so {@code lambda = log(2.0) / halfLife)}. This may be
52+ * zero, but must not be negative. The units of this value are the inverse of the units being used for time .
53+ * @param startTime The time to consider the start time for the rate calculation. This must be greater than zero. The units and origin
54+ * of this value must match all other calls to this instance .
5155 */
52- public ExponentiallyWeightedMovingRate (double lambda , long startTimeInMillis ) {
56+ public ExponentiallyWeightedMovingRate (double lambda , long startTime ) {
5357 if (lambda < 0.0 ) {
5458 throw new IllegalArgumentException ("lambda must be non-negative but was " + lambda );
5559 }
56- if (startTimeInMillis <= 0.0 ) {
57- throw new IllegalArgumentException ("startTimeInMillis must be non-negative but was " + startTimeInMillis );
60+ if (startTime <= 0.0 ) {
61+ throw new IllegalArgumentException ("startTime must be non-negative but was " + startTime );
5862 }
5963 synchronized (this ) {
6064 this .lambda = lambda ;
6165 this .rate = Double .NaN ; // should never be used
62- this .startTimeInMillis = startTimeInMillis ;
63- this .lastTimeInMillis = 0 ; // after an increment, this must be positive, so a zero value indicates we're waiting for the first
66+ this .startTime = startTime ;
67+ this .lastTime = 0 ; // after an increment, this must be positive, so a zero value indicates we're waiting for the first
6468 }
6569 }
6670
6771 /**
68- * Returns the EWMR at the given time, in millis since the epoch.
72+ * Returns the EWMR at the given time. The units and origin of this time must match all other calls to this instance. The units
73+ * of the returned rate are the ratio of the increment units to the time units as used for {@link #addIncrement}.
6974 *
7075 * <p>If there have been no increments yet, this returns zero.
7176 *
72- * <p>Otherwise, we require the time to be no earlier than the time of the previous increment, i.e. the value of {@code timeInMillis }
73- * for this call must not be less than the value of {@code timeInMillis } for the last call to {@link #addIncrement}. If this is not the
77+ * <p>Otherwise, we require the time to be no earlier than the time of the previous increment, i.e. the value of {@code time }
78+ * for this call must not be less than the value of {@code time } for the last call to {@link #addIncrement}. If this is not the
7479 * case, the method behaves as if it had that minimum value.
7580 */
76- public double getRate (long timeInMillis ) {
81+ public double getRate (long time ) {
7782 synchronized (this ) {
78- if (lastTimeInMillis == 0 ) { // indicates that no increment has happened yet
83+ if (lastTime == 0 ) { // indicates that no increment has happened yet
7984 return 0.0 ;
80- } else if (timeInMillis <= lastTimeInMillis ) {
85+ } else if (time <= lastTime ) {
8186 return rate ;
8287 } else {
8388 // This is the formula for R(t) given in subsection 2.6 of the document referenced above:
84- return expHelper (lastTimeInMillis - startTimeInMillis ) * exp (-1.0 * lambda * (timeInMillis - lastTimeInMillis )) * rate
85- / expHelper (timeInMillis - startTimeInMillis );
89+ return expHelper (lastTime - startTime ) * exp (-1.0 * lambda * (time - lastTime )) * rate / expHelper (time - startTime );
8690 }
8791 }
8892 }
8993
9094 /**
91- * Given the EWMR {@code currentRate} at time {@code currentTimeMillis } and the EWMR {@code oldRate} at time {@code oldTimeMillis},
92- * returns the EWMR that would be calculated at {@code currentTimeMillis } if the start time was {@code oldTimeMillis } rather than the
93- * {@code startTimeMillis} passed to the parameter. This rate incorporates all the increments that contributed to {@code currentRate}
94- * but not to {@code oldRate}. The increments that contributed to {@code oldRate} are effectively 'forgotten'. All times are in millis
95- * since the epoch .
95+ * Given the EWMR {@code currentRate} at time {@code currentTime } and the EWMR {@code oldRate} at time {@code oldTime}, returns the EWMR
96+ * that would be calculated at {@code currentTime } if the start time was {@code oldTime } rather than the {@code startTime} passed to the
97+ * parameter. This rate incorporates all the increments that contributed to {@code currentRate} but not to {@code oldRate}. The
98+ * increments that contributed to {@code oldRate} are effectively 'forgotten'. The units and origin of the times and rates must match
99+ * all other calls to this instance .
96100 *
97- * <p>Normally, {@code currentTimeMillis } should be after {@code oldTimeMillis }. If it is not, this method returns zero.
101+ * <p>Normally, {@code currentTime } should be after {@code oldTime }. If it is not, this method returns zero.
98102 *
99103 * <p>Note that this method does <i>not</i> depend on any of the increments made to this {@link ExponentiallyWeightedMovingRate}
100- * instance, or on its {@code startTimeMillis }. It is only non-static because it uses this instance's {@code lambda}.
104+ * instance, or on its {@code startTime }. It is only non-static because it uses this instance's {@code lambda}.
101105 */
102- public double calculateRateSince (long currentTimeMillis , double currentRate , long oldTimeMillis , double oldRate ) {
103- if (currentTimeMillis <= oldTimeMillis ) {
106+ public double calculateRateSince (long currentTime , double currentRate , long oldTime , double oldRate ) {
107+ if (currentTime <= oldTime ) {
104108 return 0.0 ;
105109 }
106110 // This is the formula for R'(t, T) given in subsection 2.7 of the document referenced above:
107- return (expHelper (currentTimeMillis - startTimeInMillis ) * currentRate - expHelper (oldTimeMillis - startTimeInMillis ) * exp (
108- -1.0 * lambda * (currentTimeMillis - oldTimeMillis )
109- ) * oldRate ) / expHelper (currentTimeMillis - oldTimeMillis );
111+ return (expHelper (currentTime - startTime ) * currentRate - expHelper (oldTime - startTime ) * exp (
112+ -1.0 * lambda * (currentTime - oldTime )
113+ ) * oldRate ) / expHelper (currentTime - oldTime );
110114 }
111115
112116 /**
113- * Updates the rate to reflect that the gauge has been incremented by an amount {@code increment} at a time {@code timeInMillis} in
114- * milliseconds since the epoch .
117+ * Updates the rate to reflect that the gauge has been incremented by an amount {@code increment} at a time {@code time}. The unit and
118+ * offset of the time must match all other calls to this instance. The units of the increment are arbitrary but must also be consistent .
115119 *
116120 * <p>If this is the first increment, we require it to occur after the start time for the rate calculation, i.e. the value of
117- * {@code timeInMillis } must be greater than {@code startTimeInMillis } passed to the constructor. If this is not the case, the method
118- * behaves as if {@code timeInMillis } is {@code startTimeInMillis + 1} to prevent a division by zero error.
121+ * {@code time } must be greater than {@code startTime } passed to the constructor. If this is not the case, the method behaves as if
122+ * {@code time } is {@code startTime + 1} to prevent a division by zero error.
119123 *
120- * <p>If this is not the first increment, we require it not to occur before the previous increment, i.e. the value of
121- * {@code timeInMillis} for this call must be greater than or equal to the value for the previous call. If this is not the case, the
122- * method behaves as if this call's {@code timeInMillis } is the same as the previous call's.
124+ * <p>If this is not the first increment, we require it not to occur before the previous increment, i.e. the value of {@code time} for
125+ * this call must be greater than or equal to the value for the previous call. If this is not the case, the method behaves as if this
126+ * call's {@code time } is the same as the previous call's.
123127 */
124- public void addIncrement (double increment , long timeInMillis ) {
128+ public void addIncrement (double increment , long time ) {
125129 synchronized (this ) {
126- if (lastTimeInMillis == 0 ) { // indicates that this is the first increment
127- if (timeInMillis <= startTimeInMillis ) {
128- timeInMillis = startTimeInMillis + 1 ;
130+ if (lastTime == 0 ) { // indicates that this is the first increment
131+ if (time <= startTime ) {
132+ time = startTime + 1 ;
129133 }
130134 // This is the formula for R(t_1) given in subsection 2.6 of the document referenced above:
131- rate = increment / expHelper (timeInMillis - startTimeInMillis );
135+ rate = increment / expHelper (time - startTime );
132136 } else {
133- if (timeInMillis < lastTimeInMillis ) {
134- timeInMillis = lastTimeInMillis ;
137+ if (time < lastTime ) {
138+ time = lastTime ;
135139 }
136140 // This is the formula for R(t_j+1) given in subsection 2.6 of the document referenced above:
137- rate += (increment - expHelper (timeInMillis - lastTimeInMillis ) * rate ) / expHelper (timeInMillis - startTimeInMillis );
141+ rate += (increment - expHelper (time - lastTime ) * rate ) / expHelper (time - startTime );
138142 }
139- lastTimeInMillis = timeInMillis ;
143+ lastTime = time ;
140144 }
141145 }
142146
0 commit comments