Что такое хороший алгоритм ограничения скорости?

Ответ: qp будет 250, если вы на машине, где int - 4 байта.

Расчет:

q - p = 1000 1000/4 (размер int) = 250

Идея:

Идея арифметики указателя заключается в том, что если у вас есть указатель int на 1000 и указатель int до 2000, и попросите разницу, вы не спрашиваете, что такое 2000-1000. Что вы спрашиваете, сколько int можно установить между ними.

Это очень удобно для всех видов операций, например:

int *i = 100;
i++; // This is **not** 101, it is 104, cause you actually want the next place an int could fit in memory.

Это особенно полезно при работе с массивами. Массив ints (определенный int arr[10]) в основном обрабатывается как указатель. Когда вы пишете arr[5], компилятор переводит его на *(arr + 5), т. Е. Добавляет 5 к указателю int с именем arr и получает значение по этому адресу.

Причина, по которой это работает , потому что arr + 5 не означает «добавить 5 к значению arr», это означает «добавить все, что является neceassary к значению arr, чтобы идти вперед 5 int s» или, точнее, «добавить 5 * sizeof(int) до значения arr "

143
задан Svante 20 March 2009 в 22:51
поделиться

6 ответов

Здесь самый простой алгоритм , если Вы хотите только отбросить сообщения, когда они прибывают слишком быстро (вместо того, чтобы ставить их в очередь, который имеет смысл, потому что очередь могла бы стать произвольно многочисленной):

rate = 5.0; // unit: messages
per  = 8.0; // unit: seconds
allowance = rate; // unit: messages
last_check = now(); // floating-point, e.g. usec accuracy. Unit: seconds

when (message_received):
  current = now();
  time_passed = current - last_check;
  last_check = current;
  allowance += time_passed * (rate / per);
  if (allowance > rate):
    allowance = rate; // throttle
  if (allowance < 1.0):
    discard_message();
  else:
    forward_message();
    allowance -= 1.0;

нет никаких datastructures, таймеры и т.д. в этом решении, и оно работает чисто:) Для наблюдения этого 'допуск' выращивает на скорости 5/8 единицы в секунды самое большее, т.е. самое большее пять единиц в восемь секунд. Каждое сообщение, которое передается, вычитает одну единицу, таким образом, Вы не можете отправить больше чем пять сообщений в каждые восемь секунд.

Примечание, которое rate должно быть целым числом, т.е. без ненулевой десятичной части или алгоритма, не будет работать правильно (фактический уровень не будет rate/per). Например, rate=0.5; per=1.0; не работает, потому что allowance никогда не будет расти до 1,0. Но rate=1.0; per=2.0; хорошо работает.

214
ответ дан cweiske 21 March 2009 в 08:51
поделиться

Используйте этого декоратора @RateLimited (ratepersec) перед Вашей функцией, которая ставит в очередь.

В основном, это проверяет, передали ли 1/уровень secs с прошлого раза, когда и в противном случае ожидает остаток времени, иначе он не ожидает. Это эффективно ограничивает Вас уровнем/секунда. Декоратор может быть применен к любой функции, которую Вы хотите ограниченный уровнем.

В Вашем случае, если Вы хотите максимум 5 сообщений в 8 секунд, @RateLimited использования (0.625) перед Вашей функцией sendToQueue.

import time

def RateLimited(maxPerSecond):
    minInterval = 1.0 / float(maxPerSecond)
    def decorate(func):
        lastTimeCalled = [0.0]
        def rateLimitedFunction(*args,**kargs):
            elapsed = time.clock() - lastTimeCalled[0]
            leftToWait = minInterval - elapsed
            if leftToWait>0:
                time.sleep(leftToWait)
            ret = func(*args,**kargs)
            lastTimeCalled[0] = time.clock()
            return ret
        return rateLimitedFunction
    return decorate

@RateLimited(2)  # 2 per second at most
def PrintNumber(num):
    print num

if __name__ == "__main__":
    print "This should print 1,2,3... at about 2 per second."
    for i in range(1,100):
        PrintNumber(i)
43
ответ дан Carlos A. Ibarra 21 March 2009 в 08:51
поделиться
  • 1
    @MattBall, Привет Матовый, мы проходим некоторые изменения. Сервис должен иначе скоро вернуться!:) – Ming Tsai 6 January 2014 в 10:42

Сохраните время, когда последние пять строк были отправлены. Держите сообщения с очередями до времени пятое новое сообщение (если оно существует), наименьшее количество 8 секунд в прошлом (с last_five как массив времен):

now = time.time()
if len(last_five) == 0 or (now - last_five[-1]) >= 8.0:
    last_five.insert(0, now)
    send_message(msg)
if len(last_five) > 5:
    last_five.pop()
2
ответ дан Pesto 21 March 2009 в 08:51
поделиться

Одно решение состоит в том, чтобы присоединить метку времени к каждому объекту очереди и отбрасывать объект после того, как 8 секунд передали. Можно выполнить эту проверку каждый раз, когда очередь добавляется к.

Это только работает, если Вы ограничиваете размер очереди 5 и отбрасываете какие-либо дополнения, пока очередь полна.

2
ответ дан jheriko 21 March 2009 в 08:51
поделиться
  • 1
    броски, если (rows<oldRows)||(cols<oldCols). Должен использовать Min(rows,oldRows) в цикле. – CodesInChaos 30 June 2011 в 18:57

Маркерный Блок довольно прост реализовать.

Запускаются с блока с 5 маркерами.

Каждый 5/8 секунды: Если блок имеет меньше чем 5 маркеров, добавьте тот.

Каждый раз Вы хотите отправить сообщение: Если блок имеет маркер ≥1, выньте один маркер и отправьте сообщение. Иначе ожидайте/отбрасывайте сообщение/независимо от того, что.

(очевидно, в фактическом коде, Вы использовали бы целочисленный счетчик вместо реальных маркеров и можно оптимизировать каждый шаг 5/8s путем хранения меток времени)

<час>

Чтение вопроса снова, если ограничение скорости полностью сбрасывается каждые 8 секунд, то вот модификация:

Запускаются с метки времени, last_send, за один раз давно (например, в эпоху). Кроме того, запустите с того же блока с 5 маркерами.

Забастовка каждое 5/8 правило секунд.

Каждый раз Вы отправляете сообщение: Во-первых, проверьте если last_send ≥ 8 секунд назад. Если так, заполнитесь, блок (установите его на 5 маркеров). Во-вторых, если существуют маркеры в блоке, отправьте сообщение (иначе, отбрасывать/ожидать/и т.д.). В-третьих, установите last_send на теперь.

, Который должен работать на тот сценарий.

<час>

я на самом деле записал бота IRC с помощью стратегии как это (первый подход). В Perl, не Python, но вот является некоторым кодом для иллюстрирования:

первая часть здесь обрабатывает добавляющие маркеры к блоку. Вы видите оптимизацию добавляющих маркеров на основе времени (2-й для длительности строки), и затем последняя строка фиксирует содержание блока к максимуму (MESSAGE_BURST)

    my $start_time = time;
    ...
    # Bucket handling
    my $bucket = $conn->{fujiko_limit_bucket};
    my $lasttx = $conn->{fujiko_limit_lasttx};
    $bucket += ($start_time-$lasttx)/MESSAGE_INTERVAL;
    ($bucket <= MESSAGE_BURST) or $bucket = MESSAGE_BURST;

, $conn является структурой данных, которая роздана. Это в методе, который обычно работает (он вычисляет, когда в следующий раз он будет иметь что-то, чтобы сделать и спит или что долго или пока это не получает сетевой трафик). Следующая часть метода обрабатывает отправку. Это скорее сложно, потому что сообщениям связали приоритеты с ними.

    # Queue handling. Start with the ultimate queue.
    my $queues = $conn->{fujiko_queues};
    foreach my $entry (@{$queues->[PRIORITY_ULTIMATE]}) {
            # Ultimate is special. We run ultimate no matter what. Even if
            # it sends the bucket negative.
            --$bucket;
            $entry->{code}(@{$entry->{args}});
    }
    $queues->[PRIORITY_ULTIMATE] = [];

Это - первая очередь, которая выполняется несмотря ни на что. Даже если это уничтожило наше соединение для лавинной рассылки. Используемый для чрезвычайно важных вещей, как ответ на PING сервера. Затем, остальная часть очередей:

    # Continue to the other queues, in order of priority.
    QRUN: for (my $pri = PRIORITY_HIGH; $pri >= PRIORITY_JUNK; --$pri) {
            my $queue = $queues->[$pri];
            while (scalar(@$queue)) {
                    if ($bucket < 1) {
                            # continue later.
                            $need_more_time = 1;
                            last QRUN;
                    } else {
                            --$bucket;
                            my $entry = shift @$queue;
                            $entry->{code}(@{$entry->{args}});
                    }
            }
    }

Наконец, состояние блока сохраняется назад к структуре данных $conn (на самом деле немного позже в методе; это сначала вычисляет, как скоро это будет иметь больше работы)

    # Save status.
    $conn->{fujiko_limit_bucket} = $bucket;
    $conn->{fujiko_limit_lasttx} = $start_time;

, Как Вы видите, фактический код обработки блока является очень маленьким — приблизительно четыре строки. Остальная часть кода является приоритетной обработкой очереди. Бот имеет приоритетные очереди так, чтобы, например, кто-то болтающий с ним не мог препятствовать тому, чтобы он делал свои важные обязанности удара/запрета.

24
ответ дан derobert 21 March 2009 в 08:51
поделиться

Как насчет этого:

long check_time = System.currentTimeMillis();
int msgs_sent_count = 0;

private boolean isRateLimited(int msgs_per_sec) {
    if (System.currentTimeMillis() - check_time > 1000) {
        check_time = System.currentTimeMillis();
        msgs_sent_count = 0;
    }

    if (msgs_sent_count > (msgs_per_sec - 1)) {
        return true;
    } else {
        msgs_sent_count++;
    }

    return false;
}
0
ответ дан 23 November 2019 в 22:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: