[erlang-questions] Minimum time for timer:sleep()?

Bob Ippolito bob@REDACTED
Mon Mar 19 23:35:06 CET 2007


On 3/19/07, James Hague <james.hague@REDACTED> wrote:
> Thanks everyone for the replies, though I still haven't worked out a
> solution I'm happy with.  Here's the details of what I'm trying to do:
>
> I have an Erlang program that communicates with an external graphics
> "driver" program.  This way I can put pretty pictures on the screen
> but without having to micromanage everything from Erlang itself.  What
> I was attempting to do was to get updates running more or less at 60
> Hz (16 microseconds per frame).
>
> My first idea was to track execution time per frame, then sleep the
> rest of the 16 millisecond slice.  This didn't work because sleep
> waits a minimum of 10 milliseconds, regardless of the value passed in.
>
> My second idea was to spawn a process at the start of a frame, wait 16
> milliseconds in that process, then send a message to the original
> process,  This works, except the 16 millisecond time can vary quite a
> bit: from 16.8 ms to 22+ ms in my tests.
>
> Any approaches to more precise timing would be appreciated!

You could write a function that sleeps as much as it can by way of
timer:sleep, and then busywaits the rest?

How about something like this:

-module(busywait).

-export([add_microsec/2, microsleep/1, test/0]).

-define(JITTER, 5).

add_microsec(Micro, {Mega0, Sec0, Micro0}) ->
    Micro1 = Micro0 + Micro,
    Sec1 = Sec0 + (Micro1 div 1000000),
    Mega1 = Mega0 + (Sec1 div 1000000),
    {Mega1, (Sec1 rem 1000000), (Micro1 rem 1000000)}.

busywait_until(Target, Loops) ->
    case now() of
	Now when Now >= Target ->
	    {Now, Loops};
	_ ->
	    erlang:yield(),
	    busywait_until(Target, 1 + Loops)
    end.

microsleep(MicroSec) ->
    Target = add_microsec(MicroSec, now()),
    AdjMsec = MicroSec - ?JITTER,
    case AdjMsec > 10000 of
	true ->
	    timer:sleep(AdjMsec div 1000);
	false ->
	    ok
    end,
    {Finish, Loops} = busywait_until(Target, 1),
    {timer:now_diff(Finish, Target), Loops}.

test() ->
    {Accuracy, Loops} = microsleep(16 * 1000),
    io:format("Jitter: ~p ms Iterations: ~p~n", [Accuracy, Loops]).



More information about the erlang-questions mailing list