1

I came across an interesting issue when discussing timing precision on the psychopy forum (psychopy is a psychology software written in python). Here's the problem:

timer=core.Clock()#instantiate a clock
stimulus.draw()#draw stimulus
win.flip()#flip the monitor to make stimulus appear
Routine = True
While Routine:
    key_press = event.getKeys(keyList=["f", "j"])#check keyboard's buffer
    if len(key_press) > 0:#keypress detected!
       RT = timer.getTime()#record response time
       Routine = False

I've been told that calling getTime() within a while loop can be dangerous : "A very tight loop hogs all the CPU time to itself, choking other processes, which might just eventually break in and seize control for quite a while to get through a backlog, completely mucking up your timing. On each iteration, call something like time.sleep(0.001) to yield time to other processes." I don't see why it would be. Can somebody shed light on this programming issue?

PeptideWitch
  • 2,239
  • 14
  • 30
user1363251
  • 421
  • 1
  • 11
  • 24
  • Possible duplicate of [How would I stop a while loop after n amount of time?](https://stackoverflow.com/questions/13293269/how-would-i-stop-a-while-loop-after-n-amount-of-time) – PeptideWitch Feb 14 '18 at 03:08

1 Answers1

1

I'll have a go at answering, based off the discussion found at How would I stop a while loop after n amount of time?, and specifically Anthony's comment: https://stackoverflow.com/a/44723559/8763097

while True: loops tend to hog the CPU processes in what, I think, is termed: Busy Waiting

Busy-waiting, busy-looping or spinning is a technique in which a process repeatedly checks to see if a condition is true, such as whether keyboard input or a lock is available.

Busy-waiting itself can be made much less wasteful by using a delay function (e.g., sleep()) found in most operating systems. This puts a thread to sleep for a specified time, during which the thread will waste no CPU time. If the loop is checking something simple then it will spend most of its time asleep and will waste very little CPU time.

So your while loop is spending a lot of time checking the if statement value of len(key_press) over and over and over. Adding a sleep()routine will allow the While loop to pause, whereby other processes can take place.

This may be why you report inconsistencies in your time function - the function .core.getTime() is not a core python function. It's part of the core of your psychopy package, and it doesn't look like a process clock...it looks like a basic clock timer. As such, the timer may be influenced by the While loop's CPU hogging. Without knowing any more about your package's clock handling, however, this is simply a guess.

Perhaps to avoid this problem, consider using time.perf_counter() with a sleep function as this will include any sleep() time within the count.

EDIT 1: Perhaps one solution is to work with the MS VC++ Runtime library, which is couched within Python's core.

import msvcrt
import time

print("GO")
start = time.perf_counter()
while True:
    if msvcrt.kbhit():
        end = time.perf_counter()
        print(end-start)
        break

The library should be able to handle the command to wait for a keyboard press before moving to the next step in the loop. Does it solve the problem? Not quite. Can the problem really be solved? I'm not sure...

PeptideWitch
  • 2,239
  • 14
  • 30
  • 1
    The idea here was to check the keyboard's buffer at a very high sampling rate. Adding a time.sleep() of say 1ms to each while loop iteration is dangerous, as the smallest interval one may sleep on a windows machine is 12-13ms (see doc). In addition, time.sleep() may vary by an arbitrary amount because of the scheduling of other activities in the system. You are correct that core.getTime() is not a python core function. The clock is based on windll.Kernel32.QueryPerformanceFrequency(byref(_qpfreq)) (for a windows machine) – user1363251 Feb 14 '18 at 22:34
  • Thanks for the explanation. I understand the problem a lot better now. Short of running your program on a linux machine and using a real time kernel (https://stackoverflow.com/questions/1133857/how-accurate-is-pythons-time-sleep) that can take you closer to 1ms, you could also try working with msvcrt (https://stackoverflow.com/questions/22365473/python-windows-msvcrt-getch-only-detects-every-3rd-keypress) or even a PyGame handler (https://stackoverflow.com/questions/24072790/detect-key-press-in-python). – PeptideWitch Feb 15 '18 at 00:23
  • Very helpful, thanks. To further clarify: I don't know if there is a timing issue. I checked CPU usage during the while loop (without any keypress), and I was around 60%, which looks fine. Peharps the first step would be to check if timing is distorted, and quantify the amplitude of this distortion. I think this is an interesting programming issue that may be helpful for the community. – user1363251 Feb 15 '18 at 01:58
  • No worries. And yes, I agree - this is an interesting little problem! Maybe someone will see this in 7 years' time and benefit from the discussion. If you're looking for a way to quantify the time discrepancies, perhaps take a look at: https://stackoverflow.com/a/1133984/8763097 or maybe even https://stackoverflow.com/a/43771965/8763097 for methods to quantify the change. – PeptideWitch Feb 15 '18 at 02:58