3

I need a counter which will get incremented as certain tasks are complete. We need the value for only last one hour, ie the window will be moving and not for static hours.

What is the best way to go about this? One way I could think of was having an array of size 60, one for each minute and update the entry for respective minute and do a total of this on a get(). On turning to a new minute, the array will be reset.

Are there better solutions? Any time based implementations of Counter available?

rajesh
  • 3,247
  • 5
  • 31
  • 56
  • 2
    Downvoter, care to comment why? – rajesh Jul 07 '16 at 07:51
  • Create an `AtomicInteger` counter and a timer service . Increment counter when ever task is complete. Using timer service reset the counter to zero after each hour. If you want to save old data put ` – sauumum Jul 07 '16 at 08:21
  • why you count it in the scale of every minute, why not just use a single (maybe long) integer? – Tiina Jul 07 '16 at 08:27
  • 1
    I don't think it is a duplicate of question mentioned as duplicate reason. Here, question is about single decaying non-exact counter, while in one proposed as duplicate it is about per-element exact expiry in the map. – Artur Biesiadowski Aug 21 '16 at 12:08

3 Answers3

3

I have written the Java class DecayingCounter for this purpose.

It uses the following formulas to implement an exponentially time-decaying counter:

tau = halfLifeInSeconds / Math.log(2.0)
value *= Math.exp(deltaTimeInNanos * -1E-9 / tau)
Christian d'Heureuse
  • 5,090
  • 1
  • 32
  • 28
2

You can implement some kind of array, which will give you best values (and can be used to draw graphs, etc). But if you are interested in doing it in single value, there is a trick to get proper approximation. It is used for example in loadavg computation on unix https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation

Systems calculate the load average as the exponentially damped/weighted moving average of the load number. The three values of load average refer to the past one, five, and fifteen minutes of system operation.[2]

Mathematically speaking, all three values always average all the system load since the system started up. They all decay exponentially, but they decay at different speed: they decay exponentially by e after 1, 5, and 15 minutes respectively. Hence, the 1-minute load average will add up 63% (more precisely: 1 - 1/e) of the load from last minute, plus 37% (1/e) of the load since start up excluding the last minute. For the 5 and 15 minute load average, the same 63%/37% ratio is computed throughout 5 minutes, and 15 minutes respectively. Therefore, it's not technically accurate that the 1-minute load average only includes the last 60 seconds activity (since it still includes 37% activity from the past), but that includes mostly the last minute.

Computing moving average on single value - there are multiple possibilities, described in detail, with some proofs here https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average

Best one for your use case is probably this one https://en.wikipedia.org/wiki/Moving_average#Application_to_measuring_computer_performance

equation

Community
  • 1
  • 1
Artur Biesiadowski
  • 3,595
  • 2
  • 13
  • 18
1

For simpler technique, but nice reading from Artur !

You could simply use a List to store every time needed. Then filter those when you get the list.

private static List<Long> counter = new LinkedList<Long>(); //Thanks to Artur to point that out. (Faster but a Queue takes more memory)
public static final long FILTER_TIME = 1000*60*60;

public static void add(){
    add(System.currentTimeMillis());
}

public static List<Long> get(){
    filter();
    return counter;
}

private static void filter(){
    int length = counter.size();
    long lastHour = System.currentTimeMillis() - 1000*60*60;

    //trim from left until value is correct or list is empty
    while(length > 0 && counter.get(0) < lastHour){
        counter.remove(0);
        length--;
    }
}

The result will be a list (sorted since the add method only add currentTime) without older value. This is basic implementation, better technics could be used but for a quick solution. This could do it.

The filter is done on the getter. This means that the list could explode in length if this reading is done rarely. So this could be done in the add method also.

AxelH
  • 14,325
  • 2
  • 25
  • 55
  • Removing old values would be probably good, otherwise list can grow forever. – Artur Biesiadowski Jul 07 '16 at 11:08
  • This is done via the subList, I setcounter before returning it ;) but I've just add a paragraph about this. – AxelH Jul 07 '16 at 11:09
  • You know that sublist is referencing original array, so old values are still there, just not accessible through that pointer? You would need something like new ArrayList(counter.subList) to get reduced subset in memory.. – Artur Biesiadowski Jul 07 '16 at 11:56
  • No I didn't know that... I never use this one actually ;) Well, a small change will do it. – AxelH Jul 07 '16 at 12:06
  • Just edited to remove myself, this is better like this. – AxelH Jul 07 '16 at 12:11
  • For small arrays it will be ok, for long ones, remove(0) on ArrayList is going to be very heavy. LinkedList would be better in such case (at cost of some more memory) – Artur Biesiadowski Jul 07 '16 at 12:18
  • Indeed the LinkedList improved the filtering time on a huge list. What's need to be know is the estimate number of call of the specific process and when the filtering will be more efficient. – AxelH Jul 07 '16 at 14:07
  • You could just as well use `java.time.Instant` objects rather than count of milliseconds from epoch. – Basil Bourque Jul 21 '16 at 07:06
  • I prefer to stay simple, Instant is from JDK8 and since I only need compareTo methods, I can easily do it myself. And I guess it take more memory since the max value is way bigger than the max value of Long. – AxelH Jul 21 '16 at 07:46