Как генерировать запросы с целевой скоростью "запросов / сек"?

Скажи, у меня есть цель x запросов / сек, которые я хочу генерировать постоянно. Моя цель - запускать эти запросы примерно с одинаковым интервалом, а не просто генерировать x запросов, а затем ждать, пока не истечет 1 секунда, и повторять все это снова и снова. Я не делаю никаких предположений об этих запросах, некоторые могут занять намного больше времени, чем другие, поэтому мой поток планировщика не будет выполнять запросы (или ждать их завершения), а передать их пулу потоков достаточного размера.

Сейчас если x в диапазоне сотен или меньше, я мог бы обойтись с.net Timerс или Thread.Sleep и проверка фактически прошедшего времени с помощью Stopwatch,

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

Расширяя первоначальный подход, я мог бы вместо этого использовать таймер для сна и сделать y запросы на каждое событие таймера, отслеживание фактических запросов в секунду, выполненных этим, и точная настройка y во время выполнения. Эффект находится где-то посередине между "положи все x запросов и подожди до истечения 1 секунды с момента запуска", чего я стараюсь не делать, и "жди более или менее ровно 1/x секунды перед началом следующего запроса".

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

Так какой самый простой способ реализовать это?

1 ответ

Решение

Один из способов сделать это:

Сначала найти (удачи в Windows) или реализовать usleep или же nanosleep функция. В качестве первого шага это может быть (на.net) простой Thread.SpinWait() / Stopwatch.Elapsed > x комбо. Если вы хотите, чтобы полюбить, сделать Thread.Sleep() если промежуток времени достаточно велик и выполняйте только точную настройку, используя Thread.SpinWait(),

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

  1. Пожарное событие
  2. Сон (время сна)

Затем каждые, скажем, 250 мс (или более для более высоких скоростей), проверяют фактически достигнутую скорость и корректируют sleepTime интервал, возможно, с некоторым сглаживанием, чтобы ослабить дикие временные колебания, как это

newRate = max(1, sleepTime / targetRate * actualRate)
sleepTime = 0.3 * sleepTime + 0.7 * newRate

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

Само собой разумеется, если ваша ставка настолько высока, что вы не можете использовать Sleep но всегда нужно вращаться, одно ядро ​​будет вращаться непрерывно. Хорошая новость: мы получаем все больше ядер на наших машинах, поэтому одно ядро ​​имеет все меньшее и меньшее значение:) Более серьезно, хотя, как вы упомянули в комментарии, если ваша программа действительно работает, у вашего генератора событий будет меньше времени (и потребуется) тратить циклы.

Проверьте https://github.com/EugenDueck/EventCannon для доказательства реализации концепции в.net. Он реализован примерно так, как описано выше, и выполнен в виде библиотеки, поэтому вы можете встроить его в свою программу, если используете.net.

Другие вопросы по тегам