2013-09-04 1 views
3

Я ищу решение для вычислить переданные байты в секунду многократно вызываемых function (см. Ниже). Из-за своей неточности я делаю не хочу просто делить переданные байты на прошедшее общее время: это привело к невозможности отображения быстрых изменений скорости после запуска в течение нескольких минут.Расчет байтов в секунду (плавный путь)

Предустановленные (вызываются примерно каждые 50 мс):

function uploadProgress(loaded, total){ 
    var bps = ?; 
    $('#elem').html(bps+' bytes per second'); 
}; 
  • Как получить средние байт в секунду для (только) за последние n секунд и это хорошая идея?
  • Какие другие методы расчета не мерцающего, но точного значения BPS доступны?
+1

Возможно, вы ищете что-то вроде скользящего среднего, что означает, что вам нужно будет отслеживать данные временных рядов. –

+1

Откуда вы получаете номера? – Pointy

+0

@MattBall, поэтому вы говорите, что я должен сохранять полученные байты каждую секунду, а затем вычислять среднее значение за последние n секунд? Pointy: цифры из сценария загрузки недоступны, если вы запрашиваете дополнительные номера: вот и все, что у меня есть. –

ответ

6

Ваша первая идея не плохая, это называется moving average, и предоставление вы называете функцию обновления через регулярные промежутки времени вам нужно только сохранить очереди (а FIFO buffer) от постоянной длины:

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 будет:

Discrete PT1 filter approximation

Где 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; 
} 

Оба решения будут давать аналогичные результаты (для вашей цели, по крайней мере), а второй, кажется, немного проще (и требует меньше памяти).

Кроме того, я бы не стал быстро обновлять значение. Фильтрация будет только «мерцать» в «качании» с частотой обновления 50 мс. Я не думаю, что кто-то ожидает, что скорость загрузки будет отображаться с частотой обновления более одного раза в секунду (или даже через пару секунд).

+1

Благодарим вас за то, что вы просветили одного из этих * ужасных программистов без обучения формальному образованию *. Вы отлично поработали, даже объяснив формулу википедии cant-wrap-my-head-arround. Я собираюсь использовать фильтр нижних частот, и он уже выглядит великолепно! –

+0

@RienNeVaPlus: рад, что это помогло! Эта формула фильтра становится намного яснее, если вы попытаетесь вычислить пару значений вручную (и, возможно, нарисуйте их на диаграмме u-y). Значение 'speed' становится все ближе и ближе к' newValue' (и теоретически никогда полностью не достигает этого). [Эта диаграмма] (http://i.imgur.com/nvjBTDh.png) показывает, как 'y' будет искать постоянный вход' u', если 'y' начинался с нуля в начале. В моем примере кода я установил 'speed' в' initialValue' в первой итерации, а затем начал фильтрацию со следующего вызова. – Groo

0

Используйте умеренное среднее значение, тогда вам не придется сохранять старые значения.

UPDATE: В основном это формула, как это:

average = new_value * factor + average_old * (100 - factor); 

Вы не должны иметь какие-либо старые ценности вокруг, они все в там в меньших и меньших пропорциях. Вы должны выбрать значение для фактор, который подходит для сочетания новых и старых значений, которые вы хотите, и как часто среднее значение обновляется.

Вот как рассчитывается среднее значение нагрузки «Unix».

+4

Уход за разработкой? Некоторый код/​​псевдокод может быть полезен. –

2

Простой фильтр нижних частот подходит только для того, чтобы убедиться, что неточности не создаются. Но если вы думаете немного глубже об измерении скорости передачи, вы получаете возможность поддерживать отдельные целые счетчики, чтобы сделать это правильно.

Если вы хотите, чтобы это было точное количество, обратите внимание на то, что имеется упрощение. Во-первых, когда речь идет о ставках, среднее арифметическое из них - это неправильная вещь для применения к байтам/сек (сек/байт более правильна - что приводит к гармоническому значению). Другая проблема заключается в том, что они должны быть взвешены. Из-за этого простое ведение счетчиков int64 байтов по сравнению с временем наблюдения фактически делает правильные вещи - как глупо, как кажется. Обычно вы взвешиваете на 1/n для каждого w. Посмотрите на аккуратные упрощения, что происходит, когда вы весите временем:

(w0 * b0/t0 + w1 * b1/t1 + w2 * b2/t2 + ...)/(w0 + w1 + w2 +. ..)

TotalBytes/

общую массу

(b0 + b1 + b2 + ...)/(w0 + w1 + w2 + ...)

Так просто держать отдельно (int64!) итоги байтов и миллисекунд. И только разделите их как шаг рендеринга, чтобы визуализировать скорость. Обратите внимание, что если вы вместо этого использовали среднее значение гармоник (которое вы должны делать для ставок - потому что вы действительно усредняете сек/байт), то это то же самое, что и время отправки байта, взвешенное по количеству байтов.

1/((w0 * t0/b0 + w1 * t1/b0 + ...)/(w0 + w1 + w2 + ...)) = TotalBytes/totalTime

Так среднее арифметическое, взвешенное по времени, совпадает с средним значением гармоник, взвешенным байтами. Просто сохраняйте общее количество байтов в одном var, а время в другом. Существует более глубокая причина того, что этот упрощенный список фактически правильный. Подумайте об интегралах. Предполагая отсутствие параллелизма, это буквально всего общее количество переданных байтов, деленное на общее время наблюдения. Предположим, что компьютер действительно занимает 1 шаг за миллисекунду и отправляет только целые байты - и вы наблюдаете весь временной интервал без пробелов. Нет приближений.

Обратите внимание, что если вы думаете об интеграле с (мсек, байт/мсек) в качестве единиц для (x, y), то область под кривой представляет собой байты, отправленные в течение периода наблюдения (точно). Вы получите тот же ответ, независимо от того, как были сделаны наблюдения. (т. е. сообщается 2x как часто).

Итак, просто сообщив (size_byte, start_ms, stop_ms), вы просто накапливаете (stop_ms-start_ms) во времени и накапливаете size_byte за наблюдение. Если вы хотите разбить эти ставки на график в минутных ведрах, тогда просто поддержите пару (байта, мс) в минуту (наблюдения).

Обратите внимание, что это тарифы для индивидуальных переводов. Индивидуальные переводы могут иметь 1 МБ/с (пользовательская точка зрения). Это тарифы, которые вы гарантируете конечным пользователям.

Вы можете оставить его здесь для простых случаев. Но делать это правильно, позволяет более интересные вещи.

С точки зрения сервера загружается. Предположим, что у двух пользователей было 1 МБ/с одновременно. Для этой статистики вам нужно вычесть время с двойным подсчетом. Если 2 пользователя выполняют 1 МБ/с одновременно в течение 1 с, то это 2 МБ/с за 1 секунду. Вам необходимо эффективно восстановить временные перекрытия и вычесть двойной счет времени.Явное регистрации в конце передачи (size_byte, start_ms, stop_ms) позволяет измерять интересные вещи:

  • Количество размещенных переводов в любой момент времени (распределение длины очереди - то есть: «я буду работать из памяти? »)
  • Пропускная способность в зависимости от количества передач (пропускная способность для длины очереди - то есть:« разрушается ли сайт, когда наше объявление отображается по телевизору? »)
  • Использование - то есть: мы переплачиваем наш провайдер облачных вычислений? "

В этой ситуации все накопленные счетчики являются точной целочисленной арифметикой. Вычитание времени с двойным подсчетом внезапно приводит к сложным алгоритмам (когда они вычисляются эффективно и в режиме реального времени).

Смежные вопросы