[hatari-devel] Higher precision when generating samples -> use nanosleep

Eero Tamminen oak at helsinkinet.fi
Wed Jan 26 22:32:24 CET 2011


Hi,

On keskiviikko 26 tammikuu 2011, Nicolas Pomarède wrote:
> Regarding SDL, I think some part of it are just not up to date to what
> recent OS/kernel can do nowadays. The fact that they don't provide a
> finer granularity than 100 ms doesn't mean it's not safe/achievable.

Sorry, I accidentally wrote its API has 100Hz accuracy, but it of course is
1000Hz as its in ms.  I.e. SDL granularity is 1ms.


> If possible and nanosleep is ok on a machine, it will be better to
> alternate between 16.66 ms and 16.67 to emulate a 16.66666... ms delay
> (60 Hz), than to alternate between 16 ms and 17 ms to also emulate
> 16.6666 ms delay.

Well, any process/thread (like X server, composite manager, SDL sound thread
etc. doing what Hatari asks them to do) will probably take at least 1ms of
time.  And then there's the monitor sync to consider...

While using nanosleep() isn't much extra CMake & C-code, I don't think it's
going to help at all with the US TOS sound delay.  That delay is in several
hundreds of ms, SDL having "just" 1ms accuracy cannot be a cause for that.



On keskiviikko 26 tammikuu 2011, Mickael Pointier wrote:
> The two points to consider are of course the frequency
> of the timer, but also the granularity, and the cost of the timer call.

The SDL functions do minimal work, the only issue is that they round
values to milliseconds:
------- SDL v1.2.14 --------
Uint32 SDL_GetTicks (void)
{
#if HAVE_CLOCK_GETTIME
        Uint32 ticks;
        struct timespec now;
        clock_gettime(CLOCK_MONOTONIC,&now);
        ticks=(now.tv_sec-start.tv_sec)*1000+(now.tv_nsec-
start.tv_nsec)/1000000;
        return(ticks);
#else
        Uint32 ticks;
        struct timeval now;
        gettimeofday(&now, NULL);
        ticks=(now.tv_sec-start.tv_sec)*1000+(now.tv_usec-
start.tv_usec)/1000;
        return(ticks);
#endif
}
-----------------------------

(Why it prefers clock_gettime() is it being monotonic I think, clock
corrections don't affect that.)

-----------------------------
void SDL_Delay (Uint32 ms)
{
#if SDL_THREAD_PTH
        pth_time_t tv;
        tv.tv_sec  =  ms/1000;
        tv.tv_usec = (ms%1000)*1000;
        pth_nap(tv);
#else
        int was_error;

#if HAVE_NANOSLEEP
        struct timespec elapsed, tv;
#else
        struct timeval tv;
        Uint32 then, now, elapsed;
#endif

        /* Set the timeout interval */
#if HAVE_NANOSLEEP
        elapsed.tv_sec = ms/1000;
        elapsed.tv_nsec = (ms%1000)*1000000;
#else
       then = SDL_GetTicks();
#endif
        do {
                errno = 0;

#if HAVE_NANOSLEEP
                tv.tv_sec = elapsed.tv_sec;
                tv.tv_nsec = elapsed.tv_nsec;
                was_error = nanosleep(&tv, &elapsed);
#else
                /* Calculate the time interval left (in case of interrupt) 
*/
                now = SDL_GetTicks();
                elapsed = (now-then);
                then = now;
                if ( elapsed >= ms ) {
                        break;
                }
                ms -= elapsed;
                tv.tv_sec = ms/1000;
                tv.tv_usec = (ms%1000)*1000;

                was_error = select(0, NULL, NULL, NULL, &tv);
#endif /* HAVE_NANOSLEEP */
       } while ( was_error && (errno == EINTR) );
#endif /* SDL_THREAD_PTH */
}
----------------------


> On Windows I tend to use QueryPerformanceCounter /
> QueryPerformanceFrequency, not only because it has a higher frequency
> than the standard millisecond timer, but also because it has a way lower
> granularity: You are guaranteed that if you call QueryperformanceCounter
> twice in a row, you get two different values, which is nice for
> profiling or measuring very short events.
> 
> Not sure for other systems.

The mentioned standard POSIX functions use highest available HW
accuracy when kernel has been configured with high resolution timers
support.  About everything should have it nowadays:
$ grep -e cpu -e clock -e resolution /proc/timer_list 
cpu: 0
 clock 0:
  .resolution: 1 nsecs
 clock 1:
  .resolution: 1 nsecs
cpu: 1
 clock 0:
  .resolution: 1 nsecs
 clock 1:
  .resolution: 1 nsecs
cpu: 2
 clock 0:
  .resolution: 1 nsecs
 clock 1:
  .resolution: 1 nsecs
cpu: 3
 clock 0:
  .resolution: 1 nsecs
 clock 1:
  .resolution: 1 nsecs


That's not much help for non-realtime process though when there are other
processes running in the system.


	- Eero



More information about the hatari-devel mailing list