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 short sleep and delay function on other C C++ libraries, but it'll crash when you put a little load which is unusable 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 takes 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.

Bellow is application of simple delayNS function. I created setIntervalNS(f,ns) for easy create thread in python with custom loop cycle down to microseconds and nanoseconds:
##### DIYDelay.py #####
import time, random;
ran=random.random;
upTIME=time.perf_counter;
upTimeNS=time.perf_counter_ns;
def Max(x,y): return (x if x>y else y);
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
#while upTime()<end : pass;#Dream Work
#while upTime()<end : continue;#Dream Work
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 :)
#while t()<end : pass;#Dream Work
#while t()<end : continue;#Dream Work
##///////////////////////////////////////
##////////// RUNTIME PACK LITE ////////// FOR PYTHON
##
import threading;
class THREAD:
def __init__(this):
this.R_onRun=None;
this.thread=None;
def run(this):
this.thread=threading.Thread(target=this.R_onRun);
this.thread.start();
def isRun(this): return this.thread.isAlive();
AIntervalNS=[];
class setIntervalNS :
def __init__(this,R_onRun,nsInterval) :
this.ns=nsInterval;
this.R_onRun=R_onRun;
this.kStop=False;
this.kPause=False;
this.thread=THREAD();
this.thread.R_onRun=this.Clock;
this.thread.run();
this.id=len(AIntervalNS); AIntervalNS.append(this);
def Clock(this) :
while not this.kPause :
this.R_onRun();
delayNS(this.ns);
def pause(this) :
this.kPause=True;
def stop(this) :
this.kPause=True;
this.kStop=True;
AIntervalNS[this.id]=None;
def resume(this) :
if (this.kPause and not this.kStop) :
this.kPause=False;
this.thread.run();
def clearAllIntervalNS():
for i in AIntervalNS:
if i!=null: i.stop();
###########
### END ### RUNTIME PACK LITE ###########
###########
from datetime import datetime;
timeNOW=datetime.now;
#####################
i=0;
nCycleUpdate=1; ### <<< 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
#print(str(Yawwwww));
#pass; ## < ~92% speed of return True
#####################
crT=upTimeNS(); crTS=timeNOW(); crTS0=crTS;
limLog=nCycleUpdate-1;
iCycle=0;
def setCycleCheck(n):
global nCycleUpdate, limLog;
nCycleUpdate=n; limLog=nCycleUpdate-1;
def logSpeed():
global crT, crTS, limLog, iCycle, nCycleUpdate, i;
iCycle+=1;
i+=1;
##DreamWorks(); ### <<< YOUR WORK < ~72% speed of not dreaming
if iCycle>limLog:
iCycle=0;
icrT=upTimeNS();
icrTS=timeNOW();
dT=(icrT-crT)/nCycleUpdate;#cycle ns per batch task completed
dTS=(icrTS-crTS).total_seconds()/nCycleUpdate;
#print("Checked: "+str(i)+" dT= "+str(dT)+" ns T= "+str(icrT/1000000000)+" s fdT= "+str(round(1000000000/Max(0.000001,dT)))+" Hz fR= "+str(1000000000*i/icrT)+" Hz" );
#print("Task: "+str(i)+" dT= "+str(dT)+" ns T= "+str(icrT/1000000000)+" s fdT= "+str(round(1000000000/Max(0.000001,dT)))+" Hz fR= "+str(1000000000*i/icrT)+" Hz dTS= "+str(dTS*1000000000)+" ns fdTS= "+str(round(1/Max(0.000000000001,dTS)))+" Hz fRS= "+str(i/((icrTS-crTS0).total_seconds()))+" Hz" );
#print("Task: "+str(i)+" dT= "+str(round(dT))+" ns T= "+str(round(icrT/1000000000,6))+" s fdT= "+str(round(1000000000/Max(0.000001,dT)))+" Hz fR= "+str(round(1000000000*i/icrT))+" Hz dTS= "+str(round(dTS*1000000000))+" ns fdTS= "+str(round(1/Max(0.000000000001,dTS)))+" Hz fRS= "+str(round(i/((icrTS-crTS0).total_seconds())))+" Hz dR= "+str(round(icrT/i))+" ns" );
#print("Task: "+str(i)+" dT= "+str(round(dT/1000,3))+" µs T= "+str(round(icrT/1000000000,6))+" s fdT= "+str(round(1000000000/Max(0.000001,dT)))+" Hz fR= "+str(round(1000000000*i/icrT))+" Hz dTS= "+str(round(dTS*1000000,3))+" µs fdTS= "+str(round(1/Max(0.000000000001,dTS)))+" Hz fRS= "+str(round(i/((icrTS-crTS0).total_seconds())))+" Hz dR= "+str(round((icrT/i)/1000,3))+" µs" );
print("Task: "+str(i)+" dT= "+str(round(dT/1000,3))+" µs T= "+str(round(icrT/1000000000,3))+" s dRealAverage= "+str(round((icrT/i)/1000,3))+" µs" );
crT=icrT;
crTS=icrTS;
#####################
#setCycleCheck(1); setIntervalNS(logSpeed,1000000000); ## 1 second
#setCycleCheck(1); setIntervalNS(logSpeed,100000000); ## 100 ms ## 10Hz
#setCycleCheck(1); setIntervalNS(logSpeed,20000000); ## 20 ms ## 50Hz
#setCycleCheck(1); setIntervalNS(logSpeed,10000000); ## 10 ms ## 100Hz ## slowdown 10.6 ms
#setCycleCheck(1); setIntervalNS(logSpeed,1000000); ## 1 ms ## measure calculate and print slowdown to 1.56 ms
#setCycleCheck(1); setIntervalNS(logSpeed,500000); ## 500 µs ## slowdown to 850 µs
#setCycleCheck(1); setIntervalNS(logSpeed,200000); ## 200 µs ## slowdown to 500 µs
setCycleCheck(1); setIntervalNS(logSpeed,100000); ## 100 µs ## slowdown to 200 µs ## Make background app and you will reach nearly pure 100µs delay
##setCycleCheck(1); setIntervalNS(logSpeed,10000); ## 10 µs ## slowdown to 100 µs
#setCycleCheck(1); setIntervalNS(logSpeed,1000); ## 1 µs ## reach 160 µs ## Limit calculate log and display task
#setCycleCheck(10); setIntervalNS(logSpeed,100000); ## 100 µs ## reach 176 µs delay silence task
##setCycleCheck(100); setIntervalNS(logSpeed,100000); ## 100 µs ## reach 106 µs delay silence task
#setCycleCheck(100); setIntervalNS(logSpeed,1000); ## 1 µs ## reach 8 µs ## Slowdown by calculate and print
#setCycleCheck(100000); setIntervalNS(logSpeed,1000); ## 1 µs ## reach 2.8 µs
#setCycleCheck(10000000); setIntervalNS(logSpeed,1000); ## 1 µs ## reach 2.75 µs
#setCycleCheck(10000000); setIntervalNS(logSpeed,100); ## 100 ns ## reach 1950 ns
#setCycleCheck(10000000); setIntervalNS(logSpeed,1); ## 1 ns ## reach 1880 ns
You can uncomment and comment code to change the print and task configuration to see accuracy efficiency.
This is the result when try delay 100µs while maximize console and display task by task with most detail:

Task: 80673 dT= 255.1 µs T= 34.029082 s fdT= 3920 Hz fR= 2371 Hz dTS= 255.0 µs fdTS= 3922 Hz fRS= 2376 Hz dR= 421.815 µs
Task: 80674 dT= 239.3 µs T= 34.029321 s fdT= 4179 Hz fR= 2371 Hz dTS= 0.0 µs fdTS= 1000000000000 Hz fRS= 2376 Hz dR= 421.813 µs
Task: 80675 dT= 251.8 µs T= 34.029573 s fdT= 3971 Hz fR= 2371 Hz dTS= 492.0 µs fdTS= 2033 Hz fRS= 2376 Hz dR= 421.811 µs
- dT : delta Time calculate by the different performance counter each cycle
- T : total Time running count by performance counter
- fdT : frequency from dT
- fR : real frequency complete task = total task complete / total time running measured by performance counter
- dTS : delta Time System calculate by get different datetime.now method
- fdTS : frequency from dTS
- fRS : real frequency calculate System = total task complete / total time running measured by datetime.now method
- dR : average delta Real delay time = total time running measured by performance counter / total task complete
So, dT is close to correct instance delay time. And using datetime or get timestamp will be slower to measure the delay.
Test delay 100µs with smaller console and simplier calculation:

Here is a test of delay 100µs which is only print out status each 100 time finish the task:
setCycleCheck(100); setIntervalNS(logSpeed,100000); ## 100 µs ## reach 104 µs delay silence task

delay 1µs:
setCycleCheck(100000); setIntervalNS(logSpeed,1000); ## 1 µs ## reach 2.3 µs

delay 100ns, it can't delay faster on my device (Intel Core i3-2370M CPU @ 2.4GHz 2.4 GHz):
setCycleCheck(10000000); setIntervalNS(logSpeed,100); ## 100 ns ## reach 1950 ns

Single delay test:

Test delay 100us with simple print task :

Finally, you can reach higher accuracy by run as background process.