Soft realtime with EventMachine and timer resolution

Hi, I trying to do music livecoding with Ruby but I am having problems
with event scheduling, I shold have known before.

My question is how to increse timer resolution for EventMachine timer.

Thing is I have a more or less precise timer (apparently a jitter of
10ms in rithmic patterns is not percieivable) but is kind of expensive
in terms of computation.

It looks somewhat like this:

   class Ticker
        attr_reader :tick

        def initialize tempo
            @tempo = tempo
            @interval = 60.0 / @tempo
            @sleep_time = @interval / 100
            @tick = 0
        end

        def run
            @thread = Thread.new do
                @next = Time.now.to_f
                loop do
                    @next += @interval
                    @tick += 1
                    Thread.new do
                        tick
                    end
                    sleep @sleep_time until Time.now.to_f >= @next
                end
            end
        end

        def tick
        end
    end

I've never used EventMachine and I don't understand much of it but I
made a timer. First thing I was surprised on how light on the processor
it is, but no matter how low is the number I pass to add_periodic_timer
I get jitter of average 50ms and If I create too much timers the jitter
regulary increases eventually reaching seconds.

Is there any way to increase timer quatum from Ruby?

I found this C line somewhere:
    evma_set_timer_quantum(16/*msec*/); // roughly 60Hz

Any advice?

Thanks
Macario
btw, heres my code:

require 'rubygems'
require 'eventmachine'

class Ticker
    attr_reader :tick

    def initialize tempo
        @tempo = tempo
        @interval = 60.0 / @tempo
        @tick = 0
    end

    def bang
        puts Time.now.to_f - @expected
        @expected = @next
    end

    def run
        @expected = Time.now.to_f
        @next = Time.now.to_f
        @thread = Thread.new do
            EventMachine.run do
                EM.add_periodic_timer do
                    if Time.now.to_f >= @next
                        @next += @interval
                        @tick += 1
                        bang
                    end
                end
            end
        end
    end
end

10.times do
    Ticker.new(120*4).run
end

sleep 10

···

--
Posted via http://www.ruby-forum.com/.

I've never used EventMachine and I don't understand much of it but I
made a timer. First thing I was surprised on how light on the processor
it is, but no matter how low is the number I pass to add_periodic_timer
I get jitter of average 50ms and If I create too much timers the jitter
regulary increases eventually reaching seconds.

Is there any way to increase timer quatum from Ruby?

I found this C line somewhere:
   evma_set_timer_quantum(16/*msec*/); // roughly 60Hz

Hehe, i thought that looked familiar... it appears to be from an email
I posted on the eventmachine-talk mailing list in 2006. :slight_smile:

But yes: these days the set_timer_quantum API is available from ruby.

  EventMachine.set_timer_quantum(10);

Note: Internally, EventMachine places limits on the min/max allowed
values for quantum: (in em.cpp)

  /* We get a timer-quantum expressed in milliseconds.
   * Don't set a quantum smaller than 5 or larger than 2500.
   */

  if ((interval < 5) || (interval > 2500))
    throw std::runtime_error ("invalid timer-quantum");

Hope this helps,

Bill

···

From: "Macario Ortega" <macarui@gmail.com>

This seem to be a recurrent mistake when implementing periodic events.
"Sleep X" does not ensure invokation every X seconds. It merely ensure
that there will be "at least X" seconds of delay between each
invokation. It could be X + 0.1, X + 1.0 or even 2 * X - there is
simply no guarantee. The code in the loop apart from 'sleep' also
takes time to execute (or the Garbage Collecter may run, or it can be
suspended by the OS while not sleeping). This time spent here may be
negilible most of the time, but OS's are not deterministic on this
scale, so it may randomly take 10ms, 100ms or more if other processes
on your computer are run at the same time. </rant>

To get any sort of accuracy, you should always sleep "until the next
event should occur" rather than a fixed time. For example:

next = Time.now
interval = 0.05
loop do
  invoke_periodic_event
  next += interval
  time_to_wait = next - Time.now
  sleep time_to_wait if time_to_wait > 0.0
end

This will not avoid the jitter, but will compensate when it occurs. If
jitter is extreme, you may want to add checks to reset the 'next'
variable to something resonable if it gets too far behind.

In music, jitter on a single beat may even add flavor to your music,
but if you shift the time scale all time the time, you are confusing
your listeners :slight_smile:

EM's add_periodic_timer is also "flawed" in this way that it doesn't
event attempt to invoke your method at fixed intervals but rather
"sleeps" a fixed interval between each invokation.

···

On Sat, Aug 8, 2009 at 7:40 AM, Macario Ortega<macarui@gmail.com> wrote:

           loop do
               @next \+= @interval
               @tick \+= 1
               Thread\.new do
                   tick
               end
               sleep @sleep\_time until Time\.now\.to\_f &gt;= @next

Bill Kelly wrote:

From: "Macario Ortega" <macarui@gmail.com>

   evma_set_timer_quantum(16/*msec*/); // roughly 60Hz

Hehe, i thought that looked familiar... it appears to be from an email
I posted on the eventmachine-talk mailing list in 2006. :slight_smile:

But yes: these days the set_timer_quantum API is available from ruby.

  EventMachine.set_timer_quantum(10);

Note: Internally, EventMachine places limits on the min/max allowed
values for quantum: (in em.cpp)

  /* We get a timer-quantum expressed in milliseconds.
   * Don't set a quantum smaller than 5 or larger than 2500.
   */

  if ((interval < 5) || (interval > 2500))
    throw std::runtime_error ("invalid timer-quantum");

Hope this helps,

Bill

Thanks for your reply, it was useful!

I found the cheapest is to exclusivelly use add_periodic_timer with my
desired interval but it increasingly drifted.

This is my timer loop:

def run
    @next = Time.now.to_f
    @thread = Thread.new do
      EventMachine.run do
        @start = Time.now.to_f
        EM.set_quantum 5
        EM.add_periodic_timer @interval / 30 do
          if Time.now.to_f >= @next
            bang
            @tick += 1
            @next = @tick * @interval + @start
          end
        end
      end
    end
    self
  end

EventMachine doesn't seem to warranty the accuracy of the periodic
timers.

···

--
Posted via http://www.ruby-forum.com/\.