我有一个调度成功和失败事件的类,我需要维护一个关于该类最后X秒内的平均失败次数/事件总数的统计数据。
我正在考虑使用循环链接列表并为每个事件附加成功或失败节点。然后计算列表中的故障节点数与总节点数,但这有两个主要缺点:
有没有人知道从最近X秒收到的样本列表中计算平均值的另一种方法?
答案 0 :(得分:7)
您应该使用采样频率(a-la MRTG)。假设您只需要一秒精度并保持过去一分钟的平均值,您将拥有一个固定的表,其中包含过去60秒(包括当前的60秒)的60个条目。并保持目前的全球进入。
每个条目都包含一个平均值和一些事件。两个值的每个条目都从0开始。
当您收到新事件时,您可以更改当前和全局条目:
average = ((number * average) + 1) / (number + 1) number = number + 1
在每个采样间隔,您使用最旧的条目更改全局条目:
global.average = ((global.number * global.average) - (oldest.number * oldest.average)) / (global.number - oldest.number) global.number = global.number - oldest.number
然后将最旧的条目重置为0并开始将其用作当前条目。
答案 1 :(得分:5)
您可以使用queue,这将允许您将新事件添加到队列的末尾,并从队列的开头删除过期事件,假设事件是按时间顺序添加的。例如,在Java中,您可以使用LinkedList
或ArrayDeque
,它们都实现Queue
接口。
如果未按时间顺序添加事件,则可以使用priority queue。元素将按其时间戳排序,并且最高优先级元素(即,要删除的下一个元素)将是具有最小时间戳的元素。在Java中,此数据结构由PriorityQueue
提供。
我们可以保留两个计数器,一个用于事件总数,另一个用于成功事件的数量,而不是定期计算事件。每当我们从队列中添加或删除事件时,这些计数器都会更新。
答案 2 :(得分:1)
将事件保存在Queue中。只需追加到最后并删除前面太旧的所有事件。这至少可以消除问题1。
答案 3 :(得分:1)
通常对于这些类型的采样器,您通常会指定一个额外的东西,那就是采样器分辨率。
在您的情况下,假设您的描述,采样器分辨率可以是1秒或1滴。
如果你想要的采样器的分辨率是1秒,那么这里的算法命题可能就足够了。
lastNode
,对第一个节点firstNode
的引用(lastNode
将是列表的尾部,{{1是最新添加的节点,头部)收到新活动时:
将事件[timestamp]与firstNode firstNode
IF(eventTimestamp.TotalSeconds> firstNode.TotalSeconds)
END IF
(每次加入活动后) REMOVE_EXPIRED_NODES
WHILE(lastNode!= nil AND curentTime.TotalSeconds - lastNode.TotalSeconds> X)
结束时
获取gFail和gSuccess时应始终以REMOVE_EXPIRED_NODES开头。
这种方法的优点:
不会从所有事件重新计算失败和成功的全局计数器,而是添加逐渐递增的事件,并在删除列表中早于X秒的节点时递减。
它使用1秒的采样器分辨率而不是存储所有事件的列表(可能是每秒数百个事件,确保每秒执行总共2个列表操作(添加+删除操作))< / p>
无论事件数量多少,平均每秒列表操作次数为2次(1次添加操作,1次删除操作)
答案 4 :(得分:1)
您的要求有多具体?如果你允许在盒子外思考一点,一个简单的盖革计数器算法,即无限脉冲响应(IIR)数字滤波器计算移动的“平均值”(取决于你如何定义“平均值”),有一个最小的内存占用,只需几行代码。
答案 5 :(得分:0)
维护两个单独的列表会更有效,一个用于成功,一个用于失败。新条目总是附加在列表的末尾(即按增加的时间戳排序)。
现在,当您想要在最后n秒获得成功/失败的数量时,您可以为now() - n
创建时间戳并处理列表。一旦找到大于此值的时间戳,就可以消除当前时间之前的所有元素。列表的长度为您提供成功或失败的数字。
如果需要优化,请查看通过减少时间戳(即预先设置新值)对列表进行排序更有效,并查看列表,直到找到时间戳小于比较值的元素。丢弃此以及所有以下成员。
事先很难说哪种情况会更有效,所以你必须尝试一下。 OTOH如果运作良好,没有理由进行优化; - )。