How to make a delay at ~100-200us
Question:
I would like to try some hardware testing with python. I have to send commands to the hardware where there is a specification for consecutive bytes transmission time interval (~100-200us). However, I found the sleep() method in time module unstable when the delay time is too small. Is there any way/operations that takes ~100-200us?
I also wonder what is the accuracy of the function time(), can it really time a 100us interval?
from time import time, sleep
a = []
start = time()
for i in range(10):
sleep(200e-6)
a.append(time()-start)
this is what ‘a’ looks like:
[0.0010325908660888672,
0.004021644592285156,
0.006222248077392578,
0.008239507675170898,
0.009252071380615234,
0.01157999038696289,
0.013728857040405273,
0.014998674392700195,
0.016725540161132812,
0.0187227725982666]
Answers:
About accuracy of time.sleep
It looks like time.sleep()
only has millisecond resolution. Though it could range between a few milliseconds to over 20 milliseconds (best case scenarios), based on what OS you are using, it does not look like you will get accurate microsecond sleep execution from it.
References
- Must See This: How accurate is python's time.sleep()?
- usleep in Python
- https://github.com/JuliaLang/julia/issues/12770
- https://gist.github.com/ufechner7/1aa3e96a8a5972864cec
Another Option:
- Take a look at the high precision timers available from winmm.lib: Creating a High-Precision, High-Resolution, and Highly Reliable Timer, Utilising Minimal CPU Resources. This resource shares some code (C++ perhaps) to create a high precision timer (they claim a 5% error) and if the precision happens to be at the order of 1 microsecond, then you probably be okay with it.
- Also see this: How to make thread sleep less than a millisecond on Windows.
You could potentially then look for creating a C/C++ wrapper around the timer-code to use it from Python.
The time module is not unstable, it is the underlying OS that you are running on. The request of sleeping is just passed on to the OS. In the documentation, one can read that the actual sleep time may be less then requested as any caught signal suspends the sleep, or it can be longer than requested as of scheduling reasons.If you need very high accuracy, you can use an Real-Time OS.
The minimum and accuracy of time.sleep is depending on the OS and python build package. But, normally it’s limited in millisecond level.
There is usleep function for microsecond sleep which is from foreign function library. But, the function is blocked and changed both behavior and accuracy across other OS, and also conflict to some python versions.
There are also several way to call other C C++ library, but it’ll crash when you put a little load which is useless for deploying application.
However, there are still tricks to reach that short of delay. There are 2 kinds using delay: delay one time and delay repeat frequently.
- If the application purpose is for testing or it only need short delay
sporadically, then we can use a simple loop to sleep and performance
counter to check the time exit sleep. (Performance counter is clock
with the highest available resolution to measure a short duration. It
does include time elapsed during sleep and is system-wide.)
- In another case, if the application need to call it very frequently and
the goal is to distribute final app to client with high performance,
CPU energy saving. We will need multiple thread and even multiple
process to apply sleep millisecond, then syncronize on starting point
with different micro…nano second and compare via performance
counter to produce stable fast clock program. This will be complex and depend on
the application to design structure.
The function for simple case is simple:
import time;
def delayNS(ns, t=time.perf_counter_ns): ## Delay at nano second
n=t(); e=n+ns;
while n<e : n=t(); ## Dream Work take so much energy :)
CAUTION that when you break down the stable minimum sleep limit, the delay effect cause by computation and OS background app and hardware communicate will rise up. You will need to short the name of class and variable and function and use simple syntax in script to reach highest speed as posible.
Below is the delay function that easier to read, but slower:
def sleepReal(dSec, upTime=time.perf_counter):## Can be short as 0.0000001 second or 100ns. The shorter, the less accurate
now=upTime(); end=now+dSec;
while now<end : now=upTime(); #Dream Work
- function time.perf_counter() will return value (in fractional
seconds) of a performance counter.
- function time.perf_counter_ns() will return value (in nanoseconds) of a performance counter. This function is better because it avoid the precision loss caused by the float type. And also faster in real test.
Before apply function, you should check the delay effect when break the limit:
##### CalSpeed.py #####
import time, random;
ran=random.random;
upTIME=time.perf_counter_ns; ## More accuracy and faster
##upTIME=time.perf_counter;
def Max(x,y): return (x if x>y else y);
#####################
i=0;
nCycleUpdate=100000; ### <<< EDIT CYCLE PRINT OUT, LET'S CHANGE IT TO 1, 10, 100, 1000, 10000, 100000, 1000000
def DreamWorks(): ### <<< YOUR DREAM WORK TO CHECK PERFORMANCE
return True;
#Yawwwww=ran()*ran(); ## < ~77% speed of return True
#pass; ## < ~92% speed of return True
#####################
crT=upTIME();
limLog=nCycleUpdate-1;
iCycle=0;
def logSpeed():
global crT, limLog, iCycle, nCycleUpdate, i;
iCycle+=1;
i+=1;
###DreamWorks(); ### <<< YOUR WORK < ~72% speed of not dreaming
if iCycle>limLog:
iCycle=0;
icrT=upTIME();
dT=(icrT-crT)/nCycleUpdate;
print("Count: "+str(i)+" dT= "+str(dT)+" ns T= "+str(icrT/1000000000)+" s fdT= "+str(round(1000000000/Max(0.001,dT)))+" Hz fR= "+str(i*1000000000/icrT)+" Hz" );
##print("Count: "+str(i)+" dT= "+str(dT*1000000000)+" ns T= "+str(icrT)+" s fdT= "+str(round(1/Max(0.000000000001,dT)))+" Hz fR= "+str(i/icrT)+" Hz" );
#print("f = "+str(i*1000000000/icrT)+" Hz"); # < not much difference due to the costly divide calculate
crT=icrT;
while True : logSpeed();
Result of check calculate speed:
When you change the nCycleUpdate larger, the display work will less and it complete more compute task. You can try resize/maximize the terminal window and add print task to see the change speed effect. If you try to paint or write file, the task complete will decrease more. So, the delay task for simple case is a check time task that has less computation step as possible.
I would like to try some hardware testing with python. I have to send commands to the hardware where there is a specification for consecutive bytes transmission time interval (~100-200us). However, I found the sleep() method in time module unstable when the delay time is too small. Is there any way/operations that takes ~100-200us?
I also wonder what is the accuracy of the function time(), can it really time a 100us interval?
from time import time, sleep
a = []
start = time()
for i in range(10):
sleep(200e-6)
a.append(time()-start)
this is what ‘a’ looks like:
[0.0010325908660888672,
0.004021644592285156,
0.006222248077392578,
0.008239507675170898,
0.009252071380615234,
0.01157999038696289,
0.013728857040405273,
0.014998674392700195,
0.016725540161132812,
0.0187227725982666]
About accuracy of time.sleep
It looks like time.sleep()
only has millisecond resolution. Though it could range between a few milliseconds to over 20 milliseconds (best case scenarios), based on what OS you are using, it does not look like you will get accurate microsecond sleep execution from it.
References
- Must See This: How accurate is python's time.sleep()?
- usleep in Python
- https://github.com/JuliaLang/julia/issues/12770
- https://gist.github.com/ufechner7/1aa3e96a8a5972864cec
Another Option:
- Take a look at the high precision timers available from winmm.lib: Creating a High-Precision, High-Resolution, and Highly Reliable Timer, Utilising Minimal CPU Resources. This resource shares some code (C++ perhaps) to create a high precision timer (they claim a 5% error) and if the precision happens to be at the order of 1 microsecond, then you probably be okay with it.
- Also see this: How to make thread sleep less than a millisecond on Windows.
You could potentially then look for creating a C/C++ wrapper around the timer-code to use it from Python.
The time module is not unstable, it is the underlying OS that you are running on. The request of sleeping is just passed on to the OS. In the documentation, one can read that the actual sleep time may be less then requested as any caught signal suspends the sleep, or it can be longer than requested as of scheduling reasons.If you need very high accuracy, you can use an Real-Time OS.
The minimum and accuracy of time.sleep is depending on the OS and python build package. But, normally it’s limited in millisecond level.
There is usleep function for microsecond sleep which is from foreign function library. But, the function is blocked and changed both behavior and accuracy across other OS, and also conflict to some python versions.
There are also several way to call other C C++ library, but it’ll crash when you put a little load which is useless for deploying application.
However, there are still tricks to reach that short of delay. There are 2 kinds using delay: delay one time and delay repeat frequently.
- If the application purpose is for testing or it only need short delay
sporadically, then we can use a simple loop to sleep and performance
counter to check the time exit sleep. (Performance counter is clock
with the highest available resolution to measure a short duration. It
does include time elapsed during sleep and is system-wide.) - In another case, if the application need to call it very frequently and
the goal is to distribute final app to client with high performance,
CPU energy saving. We will need multiple thread and even multiple
process to apply sleep millisecond, then syncronize on starting point
with different micro…nano second and compare via performance
counter to produce stable fast clock program. This will be complex and depend on
the application to design structure.
The function for simple case is simple:
import time;
def delayNS(ns, t=time.perf_counter_ns): ## Delay at nano second
n=t(); e=n+ns;
while n<e : n=t(); ## Dream Work take so much energy :)
CAUTION that when you break down the stable minimum sleep limit, the delay effect cause by computation and OS background app and hardware communicate will rise up. You will need to short the name of class and variable and function and use simple syntax in script to reach highest speed as posible.
Below is the delay function that easier to read, but slower:
def sleepReal(dSec, upTime=time.perf_counter):## Can be short as 0.0000001 second or 100ns. The shorter, the less accurate
now=upTime(); end=now+dSec;
while now<end : now=upTime(); #Dream Work
- function time.perf_counter() will return value (in fractional
seconds) of a performance counter. - function time.perf_counter_ns() will return value (in nanoseconds) of a performance counter. This function is better because it avoid the precision loss caused by the float type. And also faster in real test.
Before apply function, you should check the delay effect when break the limit:
##### CalSpeed.py #####
import time, random;
ran=random.random;
upTIME=time.perf_counter_ns; ## More accuracy and faster
##upTIME=time.perf_counter;
def Max(x,y): return (x if x>y else y);
#####################
i=0;
nCycleUpdate=100000; ### <<< EDIT CYCLE PRINT OUT, LET'S CHANGE IT TO 1, 10, 100, 1000, 10000, 100000, 1000000
def DreamWorks(): ### <<< YOUR DREAM WORK TO CHECK PERFORMANCE
return True;
#Yawwwww=ran()*ran(); ## < ~77% speed of return True
#pass; ## < ~92% speed of return True
#####################
crT=upTIME();
limLog=nCycleUpdate-1;
iCycle=0;
def logSpeed():
global crT, limLog, iCycle, nCycleUpdate, i;
iCycle+=1;
i+=1;
###DreamWorks(); ### <<< YOUR WORK < ~72% speed of not dreaming
if iCycle>limLog:
iCycle=0;
icrT=upTIME();
dT=(icrT-crT)/nCycleUpdate;
print("Count: "+str(i)+" dT= "+str(dT)+" ns T= "+str(icrT/1000000000)+" s fdT= "+str(round(1000000000/Max(0.001,dT)))+" Hz fR= "+str(i*1000000000/icrT)+" Hz" );
##print("Count: "+str(i)+" dT= "+str(dT*1000000000)+" ns T= "+str(icrT)+" s fdT= "+str(round(1/Max(0.000000000001,dT)))+" Hz fR= "+str(i/icrT)+" Hz" );
#print("f = "+str(i*1000000000/icrT)+" Hz"); # < not much difference due to the costly divide calculate
crT=icrT;
while True : logSpeed();
Result of check calculate speed:
When you change the nCycleUpdate larger, the display work will less and it complete more compute task. You can try resize/maximize the terminal window and add print task to see the change speed effect. If you try to paint or write file, the task complete will decrease more. So, the delay task for simple case is a check time task that has less computation step as possible.