我需要一个计数器,当某些任务完成时,计数器会增加。我们只需要最后一小时的值,即窗口将移动而不是静态时间。
最好的方法是什么?我能想到的一种方法是使用一个大小为60的数组,每分钟一个并更新相应分钟的条目,并在get()上执行总计。在转到新的一分钟时,阵列将被重置。
有更好的解决方案吗?任何基于时间的计数器实现可用吗?
答案 0 :(得分:2)
您可以实现某种类型的数组,它会为您提供最佳值(并可用于绘制图形等)。但是如果你有兴趣以单值进行,那就有一个技巧可以得到适当的近似值。它用于例如unix上的loadavg计算 https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation
系统将负载平均值计算为负载数的指数衰减/加权移动平均值。平均负载的三个值是指系统运行的过去的一分钟,五分钟和十五分钟。[2]
从数学上讲,自系统启动以来,所有三个值始终平均所有系统负载。它们都呈指数衰减,但它们以不同的速度衰减:它们分别在1分钟,5分钟和15分钟后以指数方式衰减。因此,1分钟的负载平均值将比最后一分钟加载63%(更确切地说:1 - 1 / e),加上自启动以来排除最后一分钟的负载的37%(1 / e)。对于5分钟和15分钟的平均负荷,在5分钟和15分钟内分别计算相同的63%/ 37%比率。因此,技术上准确的是,1分钟的平均负载仅包括最后60秒的活动(因为它仍然包括过去37%的活动),但这主要包括最后一分钟。
计算单个值的移动平均线 - 有多种可能性,详细描述,这里有一些证明 https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
最适合您的用例可能就是这个 https://en.wikipedia.org/wiki/Moving_average#Application_to_measuring_computer_performance
答案 1 :(得分:2)
为此,我编写了Java类DecayingCounter。
它使用以下公式来实现指数时间衰减计数器:
tau = halfLifeInSeconds / Math.log(2.0)
value *= Math.exp(deltaTimeInNanos * -1E-9 / tau)
答案 2 :(得分:1)
对于更简单的技术,但很好的阅读Artur!
您可以在每次需要时使用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--;
}
}
结果将是一个列表(由于add方法只添加currentTime而排序)没有旧值。这是基本的实现,可以使用更好的技术,但需要快速解决方案。这可以做到。
过滤器在getter上完成。这意味着如果很少进行此读取,则列表可能会爆炸。所以这也可以在add方法中完成。