3

INTRO: It is well known that the accuracy of time.sleep is OS and computation load dependent. The accuracy in Windows is very poor.

Similarly to /questions/17499837 a method can implement a busy wait using the time.clock method as an alternative to time.sleep. Such an approach creates unnecessary load affecting other modules in the system. That is not desirable while doing simulations.

To reduce the amount of time spent in busy wait and not relying on the time.sleep, a class employs the method select.select and exploits the timeout attribute. See code below:

from sys import platform as _platform
import time, select, socket

class HighResolutionTimeStamp():
    __init = time.clock()
    __base = time.time()

    def __init__(self):
        self.__fd = socket.socket()
        self.dtts = time.clock if _platform == 'win32' else time.time

    def __del__(self):
        self.__fd.close()

    def get_high_resolution_dt(self):
        return HighResolutionTimeStamp.__base + self.dtts() if _platform == 'win32' else time.time()

    def busy_wait(self, wait_time):
        currentTime = self.dtts()
        while (self.dtts() <= currentTime + wait_time):
            pass

    def sleep(self, wait_time):
        currentTime = self.dtts()
        while (self.dtts() < (currentTime + wait_time - 0.001)):
            select.select([self.__fd], [], [], 0.001)
        while (self.dtts() < currentTime + wait_time):
            select.select([self.__fd], [], [], 0.0)

if __name__ == '__main__':
    st = 1.0/80.0
    it = 10
    ts = 1

    time.sleep(ts)
    hrdr = HighResolutionTimeStamp()
    total = hrdr.get_high_resolution_dt()
    for i in range(it):
        hrdr.busy_wait(st)
    print 'Ellapsed:', hrdr.get_high_resolution_dt() - total

    time.sleep(ts)
    total = hrdr.get_high_resolution_dt()
    for i in range(it):
        hrdr.sleep(st)
    print 'Ellapsed:', hrdr.get_high_resolution_dt() - total

    time.sleep(ts)
    total = hrdr.get_high_resolution_dt()
    for i in range(it):
        time.sleep(st)
    print 'Ellapsed:', hrdr.get_high_resolution_dt() - total

ENVIRONMENT: I'm using PortablePython2.7.6.1

PROBLEM: When the code is executed at the PyScripter or in the command line with PyScripter open in the background, the script above performs very accurate. Once the PyScripter is closed, the method sleep becomes inaccurate. I'm aware that the timeout for select.select should be inaccurate as time.sleep but in all cases, not as described above.

RESULTS:

Without PyScripter running in the background

C:\..\PortablePython2.7.6.1\App\python.exe highresolutiondt.py

Busy wait. Ellapsed: 0.125249385834

Sleep. Ellapsed: 0.15624165535

Time.sleep. Ellapsed: 0.156844139099

With PyScripter running in the background

C:\..\PortablePython2.7.6.1\App\python.exe highresolutiondt.py

Busy wait. Ellapsed: 0.125702142715

Sleep. Ellapsed: 0.125874519348

Time.sleep. Ellapsed: 0.120799064636
Community
  • 1
  • 1
WOM
  • 31
  • 1
  • 4
  • How accurate you need it to be, and what do you get? – tmr232 Dec 23 '14 at 11:05
  • As accurate as possible. Measuring DB performance in a DB-centric architecture. Busy wait gives at least 0.001 seconds precision, but at the cost of increased load. In the code, the rate is 80Hz, or 1/80. – WOM Dec 23 '14 at 11:11
  • Why do you need a sleep for performance measurements? Do you want to time how long execution takes? There would be better approaches for that. Or do you want to simulate load? In that case, I think you could easily do with a less precise clock. – 5gon12eder Dec 23 '14 at 12:22
  • Perhaps you could use something from this PEP418 backport: https://bitbucket.org/haypo/misc/src/tip/python/pep418.py Also, see PEP418: https://www.python.org/dev/peps/pep-0418/ – ionelmc Dec 23 '14 at 12:33
  • Thanks @5gon12eder. I'm simulating a measurement system that samples data at 80Hz i.e. at 12.5ms. My application reads the dataset to store the samples individually into a database. I employ an active database to react to such data inserts. I want to guarantee, if possible, that samples are transmitted to the DB at 80Hz. Later on, I want to measure the latency and the time for in-database processing as I increase the number of measurement systems transmitting to the DB, ie, evaluate scalability. – WOM Dec 23 '14 at 14:05
  • Hi @ionelmc . Same issue using the pep418.py. On win32, it wraps the time module. – WOM Dec 23 '14 at 14:20
  • What about using [multimedia timers](http://msdn.microsoft.com/en-us/library/windows/desktop/dd743609%28v=vs.85%29.aspx) on windows? – tmr232 Dec 23 '14 at 17:09
  • @WOM, he had some unfinished code here https://bitbucket.org/haypo/misc/src/tip/python/pep418.py#cl-569 - maybe that's a start? – ionelmc Dec 23 '14 at 18:21
  • So would it help your current situation if you'd simply increase the frequency of the simulated updates until you can verify that they arrive *at least* at 80 Hz? If your database can handle updates at a rate of (100 ± 20) Hz then it can likely do so for 80 Hz. – 5gon12eder Dec 24 '14 at 07:38
  • Thanks for your suggestions @tmr232, 5gon12eder and ionelmc. I hope you had the time as well to reflect upon the effect of executing the script with pyscripter with and without executing in the background. When it does, the accuracy is high. I'm interested discussing why that behavior happens. – WOM Dec 25 '14 at 16:10

1 Answers1

3

This uses time since unix epoch, which, I'm pretty sure is more accurate, I don't use windows though, so I didn't test this out.

from time import time

def pause(secs):
    init_time = time()
    while time() < init_time+secs: pass

print("See ya in 10 seconds")
pause(10)
print("Heeeeeelooooo there")

Hope it helped

Lucas Urban
  • 627
  • 4
  • 15
  • But this will continue to use processor because of `while` loop but `time.sleep()` will not use processor intensively. – Udesh Feb 26 '23 at 10:18
  • Well yes, but that makes it check the time more frequently, thus making it more accurate. – Lucas Urban Apr 03 '23 at 17:48