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

From: “Macario O.” [email protected]

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

Bill K. wrote:

From: “Macario O.” [email protected]

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.

On Sat, Aug 8, 2009 at 7:40 AM, Macario O.[email protected] wrote:

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

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.

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.