Is Observable.Interval полезен для высокочастотных событий?

Я использую Observable.Interval для проверки того, насколько хорошо определенный фрагмент кода клиент/сервер выполняется при разных нагрузках.

Но, похоже, это нечетное поведение.

  • Observable.Interval(timespan = 0) производит события как можно быстрее, например. 8 миллионов событий в секунду. Это выглядит нормально.
  • Observable.Interval(0 < timespan < 1ms) выводит только 1 событие, а затем ничего.
  • Observable.Interval(1ms <= timespan) создает события приблизительно с запрошенной скоростью, довольно сильно квантованными и максимум до 64 событий в секунду.

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

Является ли это ожидаемым поведением, или я использую его неправильно? Если ожидается, то есть ли альтернатива Observable.Interval для имитации источников высокочастотных событий в Rx, или я должен просто свернуть свой собственный...?

Ниже показана короткая программа, демонстрирующая поведение:

static void Main(string[] args)
{
    const int millisecsPerTest = 10000;

    var intervals = new[]
    {
        TimeSpan.FromTicks(0),          // 0 -> rate of 8M messages per second
        TimeSpan.FromTicks(1000),       // 0.1ms -> rate of 0
        TimeSpan.FromTicks(20000),      // 2ms -> rate of 64 messages per second (not 500 as expected)
        TimeSpan.FromTicks(1000000),    // 100ms -> rate of 9 messages per second
    };

    foreach(var interval in intervals)
    {
        long msgs = 0;
        using (Observable.Interval(interval).Subscribe(
            l => { ++msgs; },
            e => Console.WriteLine("Error {0}", e.Message),
            () => Console.WriteLine("Completed")))
        {
            Thread.Sleep(millisecsPerTest);
        }

        Console.WriteLine("Interval: {0} ticks, Events: {1}, Rate: {2} events per second", interval.Ticks, msgs, (int)(msgs/(double)millisecsPerTest*1000));
    }
}

Ответы

Ответ 1

Да, я думаю, что часы, используемые таймерами .net, работают только через 16 мс, поэтому ваши результаты меня не удивляют. Хотя ваш тест интервала в 1 тик звучит как ошибка. Какие версии ОС, Rx,.Net вы используете? Я посмотрю, смогу ли я воспроизвести проблему.

Для случая с галочкой 0 я думаю, что вы получаете высокую пропускную способность, потому что планировщик Rx обнаруживает, что работа происходит из-за "сейчас", и сразу запускает ее и обходит таймеры .Net, чтобы запланировать работу.

Довольно легко использовать Observable.Create для создания собственной версии Interval, которая использует таймер с более высоким разрешением. Немного сложнее, но в конечном итоге более полезно было бы написать новую реализацию IScheduler, в которой использовался таймер с высоким разрешением. Затем вы можете передать этот планировщик ко всем существующим временным методам Rx.