Don Polettone Don Polettone - 1 year ago 125
Python Question

Python 2.x - sleep call at microsecond level on windows

I was given some very good hints in this forum about how to code a clock object in Python 2. I've got some code working now - it's a clock that 'ticks' at 60 FPS:

import sys
import time

class Clock(object):

def __init__(self):

self.fps = 60.0
self._tick = 1.0 / self.fps
print "TICK", self._tick
self.t = self.timestamp()

def init_os(self):

if sys.platform == "win32":
self.timestamp = time.clock
self.wait = time.sleep

def timeit(self, f, args):

t1 = self.timestamp()
t2 = self.timestamp()
return t2 - t1

def check_min_sleep(self):
"""checks the min sleep time on the system"""

runs = 1000
times = [self.timeit(self.wait, (0.001, )) for n in xrange(runs)]
average = sum(times) / runs
print "average min sleep time:", round(average, 6)
sort = sorted(times)
print "fastest, slowest", sort[0], sort[-1]

def tick(self):

next_tick = self.t + self._tick
t = self.timestamp()
while t < next_tick:
t = self.timestamp()
self.t = t

if __name__ == "__main__":
clock = Clock()

The clock does not too bad, but in order to avoid a busy loop I'd like windows to sleep less than the usual about 15 microsecs.

On my sys (Win10 64bit) it returns me an average of about 15 / 16 msecs when starting the clock if python is the only application that's running. That's way too long for a min sleep to avoid a busy loop.

Does anybody know how I can get Win to sleep less than that value?

Answer Source

You can temporarily lower the timer resolution to the wPeriodMin value returned by timeGetDevCaps. The following defines a timer_resolution context manager that calls the timeBeginPeriod and timeEndPeriod functions.

import timeit
import contextlib
import ctypes
from ctypes import wintypes

winmm = ctypes.WinDLL('winmm')

class TIMECAPS(ctypes.Structure):
    _fields_ = (('wPeriodMin', wintypes.UINT),
                ('wPeriodMax', wintypes.UINT))

def _check_time_err(err, func, args):
    if err:
        raise WindowsError('%s error %d' % (func.__name__, err))
    return args

winmm.timeGetDevCaps.errcheck = _check_time_err
winmm.timeBeginPeriod.errcheck = _check_time_err
winmm.timeEndPeriod.errcheck = _check_time_err

def timer_resolution(msecs=0):
    caps = TIMECAPS()
    winmm.timeGetDevCaps(ctypes.byref(caps), ctypes.sizeof(caps))
    msecs = min(max(msecs, caps.wPeriodMin), caps.wPeriodMax)

def min_sleep():
    setup = 'import time'
    stmt = 'time.sleep(0.001)'
    return timeit.timeit(stmt, setup, number=1000)


>>> min_sleep()

>>> with timer_resolution(msecs=1): min_sleep()

The original timer resolution is restored after the with block:

>>> min_sleep()
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download