我正在寻找一个解决方案计算重复调用的function
(下面)的每秒传输字节数。由于其不准确,我不想要简单地将传输的字节除以经过的总时间:它导致在运行几分钟后无法显示快速的速度变化。
预设(大约每50毫秒调用一次):
function uploadProgress(loaded, total){
var bps = ?;
$('#elem').html(bps+' bytes per second');
};
n
秒的平均每秒字节数,这是一个好主意吗?答案 0 :(得分:9)
你的第一个想法并不错,它被称为moving average,并且你可以定期调用你的更新函数,你只需要保持一个恒定长度的队列(FIFO buffer): / p>
var WINDOW_SIZE = 10;
var queue = [];
function updateQueue(newValue) {
// fifo with a fixed length
queue.push(newValue);
if (queue.length > WINDOW_SIZE)
queue.shift();
}
function getAverageValue() {
// if the queue has less than 10 items, decide if you want to calculate
// the average anyway, or return an invalid value to indicate "insufficient data"
if (queue.length < WINDOW_SIZE) {
// you probably don't want to throw if the queue is empty,
// but at least consider returning an 'invalid' value in order to
// display something like "calculating..."
return null;
}
// calculate the average value
var sum = 0;
for (var i = 0; i < queue.length; i++) {
sum += queue[i];
}
return sum / queue.length;
}
// calculate the speed and call `updateQueue` every second or so
var updateTimer = setInterval(..., 1000);
避免计算速度突然变化的更简单方法是使用low-pass filter。 PT1滤波器的简单离散近似是:
u[k]
是样本k
的输入(或实际值),y[k]
是输出(或过滤值)样本k
,而T
是时间常数(较大T
表示y
会更慢地跟随u
。
这将转化为:
var speed = null;
var TIME_CONSTANT = 5;
function updateSpeed(newValue) {
if (speed === null) {
speed = newValue;
} else {
speed += (newValue - speed) / TIME_CONSTANT;
}
}
function getFilteredValue() {
return speed;
}
两种解决方案都会给出类似的结果(至少为了你的目的),而后者似乎更简单(并且需要更少的内存)。
另外,我不会快速更新值。过滤只会以50ms的刷新率将“闪烁”变为“摆动”。我认为没有人希望以每秒刷新率(甚至几秒钟)的速度显示上传速度。
答案 1 :(得分:2)
一个简单的低通滤波器可以确保不会产生不准确性。但是如果你想更深入地考虑测量传输速率,你需要维护单独的整数计数器来做正确的事。
如果您希望它是精确计数,请注意可以进行简化。首先,在处理速率时,它们的算术平均值应用于字节/秒是错误的(秒/字节更正确 - 这导致调和平均值)。另一个问题是它们应该加权。因此,简单地保持int64运行的字节总数与观察时间实际上是正确的 - 这听起来很愚蠢。通常,每w的加权为1 / n。看看按时间衡量时会出现的简洁简化:
(w0 * b0 / t0 + w1 * b1 / t1 + w2 * b2 / t2 + ...)/(w0 + w1 + w2 + ...)
totalBytes /总权重
(B0 + B1 + B2 + ...)/(W0 + W1 + W2 + ...)
所以只需保持单独的(int64!)字节和毫秒的总和。并且只将它们划分为渲染步骤以显示速率。请注意,如果您改为使用调和平均值(您应该对速率执行此操作 - 因为您实际上是平均秒/字节),那么这与发送字节所需的时间相同,并按照有多少字节进行加权。 / p>
1 /((w0 * t0 / b0 + w1 * t1 / b0 + ...)/(w0 + w1 + w2 + ...))= totalBytes / TOTALTIME
因此,按时间加权的算术平均值与按字节加权的调和平均值相同。只需在一个var中保持一个运行的字节总数,在另一个var中保持时间。有一个更深层次的原因,这种简单的计数实际上是正确的。想想积分。假设没有并发性,这实际上只是传输的总字节数除以总观察时间。假设计算机实际上每毫秒需要1步,并且只发送整个字节 - 并且您可以观察整个时间间隔而没有间隙。没有近似值。
请注意,如果考虑(msec,byte / msec)的积分作为(x,y)的单位,则曲线下面积是观察期间(确切地)发送的字节数。无论观察结果如何,你都会得到相同的答案。 (即:经常报告2倍)。
因此,通过简单地报告(size_byte,start_ms,stop_ms),您只需将(stop_ms-start_ms)累积到时间并累积每观察的size_byte。如果你想将这些速率划分为分钟桶中的图形,那么只需每分钟(观察一次)保持(byte,ms)对。
请注意,这些是个人转账的费率。单个传输可能会遇到1MB / s(用户观点)。这些是您向最终用户保证的费率。
您可以将其留在这里以用于简单的案例。但这样做是正确的,可以提供更多有趣的东西。
从服务器的角度来看,加载很重要。假设有两个用户同时遇到1MB / s。对于该统计数据,您需要减去重复计算的时间。如果2个用户同时执行1MB / s的1s,那么1s的速度为2MB / s。您需要有效地重建时间重叠,并减去时间的重复计算。在传输结束时显式记录(size_byte,start_ms,stop_ms)可以测量有趣的东西:
在这种情况下,所有累积的计数器都是精确的整数运算。减去重复计算的时间突然会让你进入更复杂的算法(当有效和实时计算时)。
答案 2 :(得分:0)
使用衰减平均值,然后您不必保留旧值。
更新:基本上它是这样的公式:
average = new_value * factor + average_old * (100 - factor);
你不必保留任何旧值,它们都在较小和较小的比例。您必须为 factor 选择一个适合您想要的新旧值混合值以及平均值更新频率的值。
这就是我相信Unix“负载平均值”的计算方式。